"use client"; import { Button } from "@app/components/ui/button"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@app/components/ui/form"; import { Input } from "@app/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@app/components/ui/select"; import { toast } from "@app/hooks/useToast"; import { zodResolver } from "@hookform/resolvers/zod"; import { AxiosResponse } from "axios"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import CopyTextBox from "@app/components/CopyTextBox"; import { Credenza, CredenzaBody, CredenzaClose, CredenzaContent, CredenzaDescription, CredenzaFooter, CredenzaHeader, CredenzaTitle } from "@app/components/Credenza"; import { useOrgContext } from "@app/hooks/useOrgContext"; import { formatAxiosError } from "@app/lib/api"; import { cn } from "@app/lib/cn"; import { createApiClient } from "@app/lib/api"; import { useEnvContext } from "@app/hooks/useEnvContext"; import { ListResourcesResponse } from "@server/routers/resource"; import { Popover, PopoverContent, PopoverTrigger } from "@app/components/ui/popover"; import { CaretSortIcon } from "@radix-ui/react-icons"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@app/components/ui/command"; import { CheckIcon, ChevronsUpDown } from "lucide-react"; import { Checkbox } from "@app/components/ui/checkbox"; import { GenerateAccessTokenResponse } from "@server/routers/accessToken"; import { constructShareLink } from "@app/lib/shareLinks"; import { ShareLinkRow } from "@app/components/ShareLinksTable"; import { QRCodeCanvas, QRCodeSVG } from "qrcode.react"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@app/components/ui/collapsible"; import AccessTokenSection from "@app/components/AccessTokenUsage"; import { useTranslations } from "next-intl"; import { toUnicode } from 'punycode'; type FormProps = { open: boolean; setOpen: (open: boolean) => void; onCreated?: (result: ShareLinkRow) => void; }; export default function CreateShareLinkForm({ open, setOpen, onCreated }: FormProps) { const { org } = useOrgContext(); const { env } = useEnvContext(); const api = createApiClient({ env }); const [link, setLink] = useState(null); const [accessTokenId, setAccessTokenId] = useState(null); const [accessToken, setAccessToken] = useState(null); const [loading, setLoading] = useState(false); const [neverExpire, setNeverExpire] = useState(false); const [isOpen, setIsOpen] = useState(false); const t = useTranslations(); const [resources, setResources] = useState< { resourceId: number; name: string; niceId: string; resourceUrl: string; }[] >([]); const formSchema = z.object({ resourceId: z.number({ message: t('shareErrorSelectResource') }), resourceName: z.string(), resourceUrl: z.string(), timeUnit: z.string(), timeValue: z.coerce.number().int().positive().min(1), title: z.string().optional() }); const timeUnits = [ { unit: "minutes", name: t('minutes') }, { unit: "hours", name: t('hours') }, { unit: "days", name: t('days') }, { unit: "weeks", name: t('weeks') }, { unit: "months", name: t('months') }, { unit: "years", name: t('years') } ]; const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { timeUnit: "days", timeValue: 30, title: "" } }); useEffect(() => { if (!open) { return; } async function fetchResources() { const res = await api .get< AxiosResponse >(`/org/${org?.org.orgId}/resources`) .catch((e) => { console.error(e); toast({ variant: "destructive", title: t('shareErrorFetchResource'), description: formatAxiosError( e, t('shareErrorFetchResourceDescription') ) }); }); if (res?.status === 200) { setResources( res.data.data.resources .filter((r) => { return r.http; }) .map((r) => ({ resourceId: r.resourceId, name: r.name, niceId: r.niceId, resourceUrl: `${r.ssl ? "https://" : "http://"}${toUnicode(r.fullDomain || "")}/` })) ); } } fetchResources(); }, [open]); async function onSubmit(values: z.infer) { setLoading(true); // convert time to seconds let timeInSeconds = values.timeValue; switch (values.timeUnit) { case "minutes": timeInSeconds *= 60; break; case "hours": timeInSeconds *= 60 * 60; break; case "days": timeInSeconds *= 60 * 60 * 24; break; case "weeks": timeInSeconds *= 60 * 60 * 24 * 7; break; case "months": timeInSeconds *= 60 * 60 * 24 * 30; break; case "years": timeInSeconds *= 60 * 60 * 24 * 365; break; } const res = await api .post>( `/resource/${values.resourceId}/access-token`, { validForSeconds: neverExpire ? undefined : timeInSeconds, title: values.title || t('shareLink', {resource: (values.resourceName || "Resource" + values.resourceId)}) } ) .catch((e) => { console.error(e); toast({ variant: "destructive", title: t('shareErrorCreate'), description: formatAxiosError( e, t('shareErrorCreateDescription') ) }); }); if (res && res.data.data.accessTokenId) { const token = res.data.data; const link = constructShareLink(token.accessToken); setLink(link); setAccessToken(token.accessToken); setAccessTokenId(token.accessTokenId); const resource = resources.find( (r) => r.resourceId === values.resourceId ); onCreated?.({ accessTokenId: token.accessTokenId, resourceId: token.resourceId, resourceName: values.resourceName, resourceNiceId: resource ? resource.niceId : "", title: token.title, createdAt: token.createdAt, expiresAt: token.expiresAt }); } setLoading(false); } function getSelectedResourceName(id: number) { const resource = resources.find((r) => r.resourceId === id); return `${resource?.name}`; } return ( <> { setOpen(val); setLink(null); setLoading(false); form.reset(); }} > {t('shareCreate')} {t('shareCreateDescription')}
{!link && (
( {t('resource')} {t('resourcesNotFound')} {resources.map( ( r ) => ( { form.setValue( "resourceId", r.resourceId ); form.setValue( "resourceName", r.name ); form.setValue( "resourceUrl", r.resourceUrl ); }} > {`${r.name}`} ) )} )} /> ( {t('shareTitleOptional')} )} />
{t('expireIn')}
( )} /> ( )} />
setNeverExpire( val as boolean ) } />

{t('shareExpireDescription')}

)} {link && (

{t('shareSeeOnce')}

{t('shareAccessHint')}

{accessTokenId && accessToken && (
)}
)}
); }