Path: blob/main/components/dashboard/src/teams/variables/NamedOrganizationEnvvarItem.tsx
2501 views
/**1* Copyright (c) 2025 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { OrganizationEnvironmentVariable } from "@gitpod/public-api/lib/gitpod/v1/envvar_pb";7import { useCallback, useState } from "react";8import { OrganizationRemoveEnvvarModal } from "./OrganizationRemoveEnvvarModal";9import { InputField } from "../../components/forms/InputField";10import { ReactComponent as Stack } from "../../icons/Repository.svg";11import { Button } from "@podkit/buttons/Button";12import { useCreateOrganizationEnvironmentVariable } from "../../data/organizations/org-envvar-queries";13import Modal, { ModalBody, ModalFooter, ModalFooterAlert, ModalHeader } from "../../components/Modal";14import { TextInputField } from "../../components/forms/TextInputField";15import { useToast } from "../../components/toasts/Toasts";16import { LoadingButton } from "@podkit/buttons/LoadingButton";17import { MiddleDot } from "../../components/typography/MiddleDot";1819type Props = {20disabled?: boolean;21organizationId: string;22name: string;23variable: OrganizationEnvironmentVariable | undefined;24};25export const NamedOrganizationEnvvarItem = ({ disabled, organizationId, name, variable }: Props) => {26const [showRemoveModal, setShowRemoveModal] = useState<boolean>(false);27const [showAddModal, setShowAddModal] = useState<boolean>(false);2829const value = variable ? "*****" : "not set";3031return (32<>33{variable && showRemoveModal && (34<OrganizationRemoveEnvvarModal35variable={variable}36organizationId={organizationId}37onClose={() => setShowRemoveModal(false)}38/>39)}4041{showAddModal && (42<AddOrgEnvironmentVariableModal43organizationId={organizationId}44staticName={name}45onClose={() => setShowAddModal(false)}46/>47)}4849<InputField disabled={disabled} className="w-full max-w-lg">50<div className="flex flex-col bg-pk-surface-secondary p-3 rounded-lg">51<div className="flex items-center justify-between">52<div className="flex-1 flex items-center overflow-hidden h-8 gap-2" title={value}>53<Stack className="w-5 h-5" />54<span className="truncate font-medium text-pk-content-secondary">{name}</span>55</div>56{!disabled && !variable && (57<Button variant="link" onClick={() => setShowAddModal(true)}>58Add59</Button>60)}61{!disabled && variable && (62<Button variant="link" onClick={() => setShowRemoveModal(true)}>63Delete64</Button>65)}66</div>67<div className="mx-7 text-pk-content-tertiary truncate">68{value}69{disabled && (70<>71<MiddleDot />72Requires <span className="font-medium">Owner</span> permissions to change73</>74)}75</div>76</div>77</InputField>78</>79);80};8182type AddOrgEnvironmentVariableModalProps = {83organizationId: string;84staticName?: string;85onClose: () => void;86};87export const AddOrgEnvironmentVariableModal = ({88organizationId,89staticName,90onClose,91}: AddOrgEnvironmentVariableModalProps) => {92const { toast } = useToast();9394const [name, setName] = useState(staticName || "");95const [value, setValue] = useState("");96const createVariable = useCreateOrganizationEnvironmentVariable();9798const addVariable = useCallback(() => {99createVariable.mutateAsync(100{101organizationId,102name,103value,104},105{106onSuccess: () => {107toast("Variable added");108onClose();109},110},111);112}, [createVariable, organizationId, name, value, onClose, toast]);113114return (115<Modal visible onClose={onClose} onSubmit={addVariable}>116<ModalHeader>Add a variable</ModalHeader>117<ModalBody>118<div>119<TextInputField120disabled={staticName !== undefined}121label="Name"122autoComplete={"off"}123className="w-full"124value={name}125placeholder="Variable name"126onChange={(name) => setName(name)}127autoFocus128required129/>130</div>131<div className="mt-4">132<TextInputField133label="Value"134autoComplete={"off"}135className="w-full"136value={value}137placeholder="Variable value"138onChange={(value) => setValue(value)}139required140/>141</div>142</ModalBody>143<ModalFooter144alert={145createVariable.isError ? (146<ModalFooterAlert type="danger">147{String(createVariable.error).replace(/Error: Request \w+ failed with message: /, "")}148</ModalFooterAlert>149) : null150}151>152<Button variant="secondary" onClick={onClose}>153Cancel154</Button>155<LoadingButton type="submit" loading={createVariable.isLoading}>156Add Variable157</LoadingButton>158</ModalFooter>159</Modal>160);161};162163164