mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-24 12:22:01 +00:00
6e47d76ba6
Client file sharing, rebuilt from the ground up: a private area per client, resumable uploads, folders, groups and categories, sharing with expiry dates and download limits, comments, file versions, an activity log, a REST API, and sixteen languages. This repository begins here. ProjectSend 2 was developed privately, and that development history is not published — the previous generation remains available, with its own history, at projectsend/legacy. Free software under the GNU General Public License v2, or (at your option) any later version.
82 lines
3.4 KiB
TypeScript
82 lines
3.4 KiB
TypeScript
import { useState } from 'react';
|
|
|
|
import InputError from '@/components/input-error';
|
|
import { Checkbox } from '@/components/ui/checkbox';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Label } from '@/components/ui/label';
|
|
import { useTranslation } from '@/hooks/use-translation';
|
|
import { slugify } from '@/lib/utils';
|
|
|
|
interface GroupFormProps {
|
|
name: string;
|
|
slug: string;
|
|
description: string;
|
|
isPublic: boolean;
|
|
onChange: (field: 'name' | 'slug' | 'description' | 'public', value: string | boolean) => void;
|
|
errors: Partial<Record<string, string>>;
|
|
}
|
|
|
|
export function GroupForm({ name, slug, description, isPublic, onChange, errors }: GroupFormProps) {
|
|
const { t } = useTranslation();
|
|
// Auto-fill the slug from the name until the user edits it directly
|
|
// (an already-populated slug, e.g. when editing, counts as touched
|
|
// so we never clobber a deliberate value).
|
|
const [slugTouched, setSlugTouched] = useState(slug !== '');
|
|
|
|
return (
|
|
<div className="grid max-w-md gap-6">
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="name">{t('Name')}</Label>
|
|
<Input
|
|
id="name"
|
|
value={name}
|
|
onChange={(e) => onChange('name', e.target.value)}
|
|
onBlur={() => {
|
|
if (!slugTouched) onChange('slug', slugify(name));
|
|
}}
|
|
required
|
|
/>
|
|
<InputError message={errors.name} />
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
<Label htmlFor="description">{t('Description')}</Label>
|
|
<Input id="description" value={description} onChange={(e) => onChange('description', e.target.value)} />
|
|
<InputError message={errors.description} />
|
|
</div>
|
|
|
|
<div className="grid gap-2">
|
|
<div className="flex items-start gap-2">
|
|
<Checkbox id="public" checked={isPublic} onCheckedChange={(checked) => onChange('public', checked === true)} />
|
|
<div className="grid gap-1">
|
|
<Label htmlFor="public" className="font-normal">
|
|
{t('Public group')}
|
|
</Label>
|
|
<p className="text-muted-foreground text-sm">
|
|
{t('The files of a public group can be browsed by anyone with the link, without logging in.')}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<InputError message={errors.public} />
|
|
|
|
{isPublic && (
|
|
<div className="mt-2 grid gap-2 pl-6">
|
|
<Label htmlFor="slug">{t('URL slug')}</Label>
|
|
<Input
|
|
id="slug"
|
|
value={slug}
|
|
onChange={(e) => {
|
|
setSlugTouched(true);
|
|
onChange('slug', e.target.value);
|
|
}}
|
|
required
|
|
/>
|
|
<p className="text-muted-foreground text-sm">{t('Used in the public group URL when this group is public.')}</p>
|
|
<InputError message={errors.slug} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|