import { Toggle } from "@/components/ui/toggle"; import { Check, Info } from "@/components/icons/platform-icons"; import type { ClassicConfigState, EnvRow, PortProtocol, PortRow, VolumeRow, } from "@/modules/apps/components/configurator/configurator-mapper"; const NETWORK_OPTIONS = ["bridge", "host"] as const; const PORT_PROTOCOL_OPTIONS = ["TCP", "UDP", "TCP + UDP"] as const; type ClassicFormViewProps = { appIdLabel?: string; state: ClassicConfigState; onChange: (state: ClassicConfigState) => void; }; function nextId(prefix: string) { return `${prefix}-${Math.random().toString(36).slice(2, 8)}`; } export function ClassicFormView({ appIdLabel, state, onChange, }: ClassicFormViewProps) { const update = (patch: Partial) => { onChange({ ...state, ...patch }); }; return (
{appIdLabel ? (
) : null}
update({ dockerImage: value })} placeholder="e.g. nginx:latest" success /> update({ title: value })} placeholder="e.g. Your App Name" success />
{state.iconUrl ? ( // eslint-disable-next-line @next/next/no-img-element {`${state.title ) : ( N/A )}
update({ iconUrl: value })} placeholder="https://..." />
update({ webUi: { ...state.webUi, host: value, }, }) } placeholder="localhost" />
update({ webUi: { ...state.webUi, port: value.replace(/[^0-9]/g, ""), }, }) } placeholder="8080" />
update({ webUi: { ...state.webUi, path: value, }, }) } placeholder="/index.html" />
update({ webUiLink: value })} placeholder="https://myapp.example.com" />

Where the Open button sends you. Leave empty to use this server's address. Set it when the app is published through a tunnel or reverse proxy.

update({ restartPolicy: value })} placeholder="always" /> update({ capabilities: value })} placeholder="NET_ADMIN,SYS_TIME" /> update({ hostname: value })} placeholder="Hostname of app container" success />
); } function Field({ label, children, withAdd = false, onAdd, }: { label: string; children: React.ReactNode; withAdd?: boolean; onAdd?: () => void; }) { return (
{withAdd && ( )}
{children}
); } function Input({ ariaLabel, value, onChange, placeholder, readOnly = false, success = false, }: { ariaLabel?: string; value: string; onChange: (value: string) => void; placeholder?: string; readOnly?: boolean; success?: boolean; }) { return (
onChange(event.target.value)} placeholder={placeholder} readOnly={readOnly} className={`h-9 w-full rounded-lg border bg-secondary/35 px-2.5 pr-8 text-xs text-foreground placeholder:text-muted-foreground/70 focus:outline-none ${ success ? "border-status-green/70 focus:border-status-green" : "border-glass-border focus:border-primary/50" } ${readOnly ? "opacity-80" : ""}`} /> {success ? ( ) : null}
); } function Select({ value, options, onChange, ariaLabel, }: { value: string; options: Array<{ value: string; label: string }>; onChange: (value: string) => void; ariaLabel: string; }) { return ( ); } function HintText() { return (

Click "+" to add one.

); } function PortEditor({ row, onRemove, onChange, }: { row: PortRow; onRemove: () => void; onChange: (row: PortRow) => void; }) { return (
onChange({ ...row, host: value.replace(/[^0-9]/g, "") }) } placeholder="Host" /> onChange({ ...row, container: value.replace(/[^0-9]/g, "") }) } placeholder="Container" /> onChange({ ...row, host: value })} placeholder="Host" /> onChange({ ...row, container: value })} placeholder="Container" />
); } function EnvEditor({ row, onRemove, onChange, }: { row: EnvRow; onRemove: () => void; onChange: (row: EnvRow) => void; }) { return (
onChange({ ...row, key: value })} placeholder="KEY" /> onChange({ ...row, value })} placeholder="VALUE" />
); } function StringListEditor({ values, placeholder, onChange, }: { values: string[]; placeholder: string; onChange: (values: string[]) => void; }) { if (values.length === 0) { return ; } return (
{values.map((value, index) => (
onChange( values.map((entry, currentIndex) => currentIndex === index ? nextValue : entry, ), ) } placeholder={placeholder} />
))}
); }