feat: recreate form collection (layout + create)

This commit is contained in:
Sonny
2024-11-01 17:42:40 +01:00
committed by Sonny
parent 1a102ebc5f
commit e77b7cc176
9 changed files with 292 additions and 5 deletions

View File

@@ -1,5 +1,5 @@
const PATHS = {
AUTHOR: 'https://www.sonny.dev/',
AUTHOR: 'https://www.sonny.dev/?utm_source=mylinks',
REPO_GITHUB: 'https://github.com/Sonny93/my-links',
EXTENSION:
'https://chromewebstore.google.com/detail/mylinks/agkmlplihacolkakgeccnbhphnepphma',

View File

@@ -36,7 +36,7 @@ export default class CollectionsController {
// Create collection form
async showCreatePage({ inertia, auth }: HttpContext) {
const collections = await this.getCollectionsByAuthorId(auth.user!.id);
return inertia.render('collections/create', {
return inertia.render('mantine/collections/create', {
disableHomeLink: collections.length === 0,
});
}

View File

@@ -1,4 +1,5 @@
.footer {
width: 100%;
margin-top: rem(40px);
border-top: rem(1px) solid
light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-5));

View File

@@ -1,9 +1,11 @@
import PATHS from '#constants/paths';
import { Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { Anchor, Group, Image, Text } from '@mantine/core';
import { Anchor, Group, Text } from '@mantine/core';
import { useTranslation } from 'react-i18next';
import ExternalLink from '~/components/common/external_link';
import { MantineLanguageSwitcher } from '~/components/common/mantine_language_switcher';
import { MantineThemeSwitcher } from '~/components/common/mantine_theme_switcher';
import packageJson from '../../../package.json';
import classes from './footer.module.css';
@@ -31,8 +33,6 @@ export function MantineFooter() {
return (
<div className={classes.footer}>
<div className={classes.inner}>
<Image src="/logo-light.png" h={40} alt="MyLinks's logo" />
<Group gap={4} c="dimmed">
<Text size="sm">{t('footer.made_by')}</Text>{' '}
<Anchor size="sm" component={ExternalLink} href={PATHS.AUTHOR}>
@@ -44,6 +44,11 @@ export function MantineFooter() {
</Anchor>
</Group>
<Group gap="sm" mt={4} mb={4}>
<MantineThemeSwitcher />
<MantineLanguageSwitcher />
</Group>
<Group gap="xs" justify="flex-end" wrap="nowrap">
{items}
</Group>

View File

@@ -29,6 +29,24 @@
"delete-confirm": "Confirm deletion?",
"delete-description": "You must delete all links in this collection before you can delete this collection."
},
"form": {
"name": "Name",
"description": "Description",
"visibility": "Visibility",
"visibility-warning": "The content will be visible to everyone",
"create": "Create",
"update": "Update",
"delete": "Delete",
"cancel": "Cancel",
"save": "Save",
"close": "Close",
"confirm": "Confirm",
"delete-confirm": "Confirmer deletion?"
},
"visibility": {
"private": "Privé",
"public": "Public"
},
"favorite": "Favorite",
"add-favorite": "Add to favorites",
"remove-favorite": "Remove from favorites",

View File

@@ -29,6 +29,24 @@
"delete-confirm": "Confirmer la suppression ?",
"delete-description": "Vous devez supprimer tous les liens de cette collection avant de pouvoir supprimer cette collection"
},
"form": {
"name": "Nom",
"description": "Description",
"visibility": "Visibilité",
"visibility-warning": "Le contenu sera visible par tout le monde",
"create": "Créer",
"update": "Mettre à jour",
"delete": "Supprimer",
"cancel": "Annuler",
"save": "Sauvegarder",
"close": "Fermer",
"confirm": "Confirmer",
"delete-confirm": "Confirmer la suppression ?"
},
"visibility": {
"private": "Privé",
"public": "Public"
},
"favorite": "Favoris",
"add-favorite": "Ajouter aux favoris",
"remove-favorite": "Retirer des favoris",

View File

@@ -0,0 +1,101 @@
import { Visibility } from '#enums/visibility';
import { Box, Group, SegmentedControl, Text, TextInput } from '@mantine/core';
import { FormEvent } from 'react';
import { useTranslation } from 'react-i18next';
import BackToDashboard from '~/components/common/navigation/back_to_dashboard';
import { MantineFormLayout } from '~/mantine/layouts/mantine_form_layout';
import { Collection } from '~/types/app';
export type FormCollectionData = {
name: string;
description: string | null;
visibility: Visibility;
nextId?: Collection['id'];
};
interface FormCollectionProps {
title: string;
canSubmit: boolean;
disableHomeLink?: boolean;
data: FormCollectionData;
errors?: Record<string, Array<string>>;
disableInputs?: boolean;
submitBtnDanger?: boolean;
setData: (name: string, value: any) => void;
handleSubmit: () => void;
}
export default function MantineFormCollection({
title,
canSubmit,
disableHomeLink,
data,
errors,
disableInputs = false,
submitBtnDanger = false,
setData,
handleSubmit,
}: FormCollectionProps) {
const { t } = useTranslation('common');
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
handleSubmit();
};
return (
<MantineFormLayout
title={title}
handleSubmit={onSubmit}
canSubmit={canSubmit}
disableHomeLink={disableHomeLink}
submitBtnDanger={submitBtnDanger}
>
<BackToDashboard>
<TextInput
label={t('form.name')}
placeholder={t('form.name')}
onChange={({ target }) => setData('name', target.value)}
value={data.name}
disabled={disableInputs}
error={errors?.name}
mt="md"
autoFocus
required
/>
<TextInput
label={t('form.description')}
placeholder={t('form.description')}
onChange={({ target }) => setData('description', target.value)}
value={data.description ?? undefined}
disabled={disableInputs}
error={errors?.description}
mt="md"
/>
<Box mt="md">
<Text size="sm" fw={500} mb={3}>
{t('form.visibility')}
</Text>
<Group wrap="nowrap">
<SegmentedControl
data={[
{ label: t('visibility.private'), value: Visibility.PRIVATE },
{ label: t('visibility.public'), value: Visibility.PUBLIC },
]}
onChange={(value) => setData('visibility', value as Visibility)}
value={data.visibility}
style={{ minWidth: 'fit-content' }}
/>
{data.visibility === Visibility.PUBLIC && (
<Text c="dimmed" size="sm">
{t('form.visibility-warning')}
</Text>
)}
</Group>
</Box>
</BackToDashboard>
</MantineFormLayout>
);
}

View File

@@ -0,0 +1,103 @@
import { Head, Link } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { Anchor, Button, Container, Group, rem, Title } from '@mantine/core';
import { FormEvent, PropsWithChildren } from 'react';
import { useTranslation } from 'react-i18next';
import { MantineFooter } from '~/components/footer/mantine_footer';
import i18n from '~/i18n';
import { appendCollectionId } from '~/lib/navigation';
import BaseLayout from '~/mantine/layouts/_mantine_base_layout';
interface FormLayoutProps extends PropsWithChildren {
title: string;
canSubmit: boolean;
handleSubmit: (event: FormEvent<HTMLFormElement>) => void;
textSubmitButton?: string;
disableHomeLink?: boolean;
submitBtnDanger?: boolean;
collectionId?: number;
}
function MantineFormLayout({
title,
children,
canSubmit,
handleSubmit,
textSubmitButton = i18n.t('common:form.create'),
disableHomeLink = false,
submitBtnDanger = false,
collectionId,
}: FormLayoutProps) {
const { t } = useTranslation('common');
return (
<Container
style={{
minHeight: '100%',
display: 'flex',
flexDirection: 'column',
}}
pt={80}
>
<main
style={{
display: 'flex',
flex: 1,
alignItems: 'center',
flexDirection: 'column',
}}
>
<Head title={title} />
<form
onSubmit={handleSubmit}
style={{
maxWidth: '100%',
width: rem(600),
}}
>
<Title order={1} size="h2">
{title}
</Title>
{children}
<Group
justify={disableHomeLink ? 'flex-end' : 'space-between'}
align="center"
mt="md"
>
{!disableHomeLink && (
<Anchor
component={Link}
href={appendCollectionId(route('dashboard').url, collectionId)}
disabled={disableHomeLink}
>
{t('back-home')}
</Anchor>
)}
<Button
type="submit"
variant="filled"
disabled={!canSubmit}
color={submitBtnDanger ? 'red' : 'blue'}
style={{ transition: 'background-color 0.15s' }}
>
{textSubmitButton}
</Button>
</Group>
<MantineFooter />
</form>
</main>
</Container>
);
}
const LayoutWrapper = (props: FormLayoutProps) => (
<BaseLayout>
<MantineFormLayout {...props} />
</BaseLayout>
);
export { LayoutWrapper as MantineFormLayout };

View File

@@ -0,0 +1,41 @@
import { Visibility } from '#enums/visibility';
import { useForm } from '@inertiajs/react';
import { route } from '@izzyjs/route/client';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import MantineFormCollection, {
FormCollectionData,
} from '~/mantine/components/form/mantine_form_collection';
export default function CreateCollectionPage({
disableHomeLink,
}: {
disableHomeLink: boolean;
}) {
const { t } = useTranslation('common');
const { data, setData, submit, processing } = useForm<FormCollectionData>({
name: '',
description: '',
visibility: Visibility.PRIVATE,
});
const isFormDisabled = useMemo(
() => processing || data.name.length === 0,
[processing, data]
);
const handleSubmit = () => {
const { method, url } = route('collection.create');
submit(method, url);
};
return (
<MantineFormCollection
title={t('collection.create')}
canSubmit={!isFormDisabled}
disableHomeLink={disableHomeLink}
data={data}
setData={setData}
handleSubmit={handleSubmit}
/>
);
}