mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-09 23:15:36 +00:00
feat: add delete collection form and controller method
This commit is contained in:
@@ -2,6 +2,7 @@ import Collection from '#models/collection';
|
|||||||
import User from '#models/user';
|
import User from '#models/user';
|
||||||
import {
|
import {
|
||||||
createCollectionValidator,
|
createCollectionValidator,
|
||||||
|
deleteCollectionValidator,
|
||||||
updateCollectionValidator,
|
updateCollectionValidator,
|
||||||
} from '#validators/collection';
|
} from '#validators/collection';
|
||||||
import type { HttpContext } from '@adonisjs/core/http';
|
import type { HttpContext } from '@adonisjs/core/http';
|
||||||
@@ -82,6 +83,28 @@ export default class CollectionsController {
|
|||||||
return this.redirectToCollectionId(response, params.id);
|
return this.redirectToCollectionId(response, params.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async showDeletePage({ auth, request, inertia, response }: HttpContext) {
|
||||||
|
const collectionId = request.qs()?.collectionId;
|
||||||
|
if (!collectionId) {
|
||||||
|
return response.redirectToNamedRoute('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
|
const collection = await this.getCollectionById(
|
||||||
|
collectionId,
|
||||||
|
auth.user!.id
|
||||||
|
);
|
||||||
|
return inertia.render('collections/delete', {
|
||||||
|
collection,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete({ request, auth, response }: HttpContext) {
|
||||||
|
const { params } = await request.validateUsing(deleteCollectionValidator);
|
||||||
|
const collection = await this.getCollectionById(params.id, auth.user!.id);
|
||||||
|
await collection.delete();
|
||||||
|
return response.redirectToNamedRoute('dashboard');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get collection by id.
|
* Get collection by id.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
import vine, { SimpleMessagesProvider } from '@vinejs/vine';
|
||||||
import { Visibility } from '../enums/visibility.js';
|
import { Visibility } from '../enums/visibility.js';
|
||||||
|
|
||||||
|
const params = vine.object({
|
||||||
|
id: vine.string().trim(),
|
||||||
|
});
|
||||||
|
|
||||||
export const createCollectionValidator = vine.compile(
|
export const createCollectionValidator = vine.compile(
|
||||||
vine.object({
|
vine.object({
|
||||||
name: vine.string().trim().minLength(1).maxLength(254),
|
name: vine.string().trim().minLength(1).maxLength(254),
|
||||||
@@ -17,9 +21,13 @@ export const updateCollectionValidator = vine.compile(
|
|||||||
visibility: vine.enum(Visibility),
|
visibility: vine.enum(Visibility),
|
||||||
nextId: vine.string().optional(),
|
nextId: vine.string().optional(),
|
||||||
|
|
||||||
params: vine.object({
|
params,
|
||||||
id: vine.string().trim(),
|
})
|
||||||
}),
|
);
|
||||||
|
|
||||||
|
export const deleteCollectionValidator = vine.compile(
|
||||||
|
vine.object({
|
||||||
|
params,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,31 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const Button = styled.button(({ theme }) => ({
|
const Button = styled.button<{ danger?: boolean }>(({ theme, danger }) => {
|
||||||
cursor: 'pointer',
|
const btnColor = !danger ? theme.colors.primary : theme.colors.lightRed;
|
||||||
width: '100%',
|
const btnDarkColor = !danger ? theme.colors.darkBlue : theme.colors.lightRed;
|
||||||
textTransform: 'uppercase',
|
return {
|
||||||
fontSize: '14px',
|
cursor: 'pointer',
|
||||||
color: theme.colors.white,
|
width: '100%',
|
||||||
background: theme.colors.primary,
|
textTransform: 'uppercase',
|
||||||
padding: '0.75em',
|
fontSize: '14px',
|
||||||
border: `1px solid ${theme.colors.primary}`,
|
|
||||||
borderRadius: theme.border.radius,
|
|
||||||
transition: theme.transition.delay,
|
|
||||||
|
|
||||||
'&:disabled': {
|
|
||||||
cursor: 'not-allowed',
|
|
||||||
opacity: '0.5',
|
|
||||||
},
|
|
||||||
|
|
||||||
'&:not(:disabled):hover': {
|
|
||||||
boxShadow: `${theme.colors.darkBlue} 0 0 3px 1px`,
|
|
||||||
background: theme.colors.darkBlue,
|
|
||||||
color: theme.colors.white,
|
color: theme.colors.white,
|
||||||
},
|
background: btnColor,
|
||||||
}));
|
padding: '0.75em',
|
||||||
|
border: `1px solid ${btnColor}`,
|
||||||
|
borderRadius: theme.border.radius,
|
||||||
|
transition: theme.transition.delay,
|
||||||
|
|
||||||
|
'&:disabled': {
|
||||||
|
cursor: 'not-allowed',
|
||||||
|
opacity: '0.5',
|
||||||
|
},
|
||||||
|
|
||||||
|
'&:not(:disabled):hover': {
|
||||||
|
boxShadow: `${btnDarkColor} 0 0 3px 1px`,
|
||||||
|
background: btnDarkColor,
|
||||||
|
color: theme.colors.white,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
export default Button;
|
export default Button;
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ const Input = styled.input(({ theme }) => ({
|
|||||||
borderBottom: `2px solid ${theme.colors.primary}`,
|
borderBottom: `2px solid ${theme.colors.primary}`,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'&:disabled': {
|
||||||
|
opacity: 0.5,
|
||||||
|
},
|
||||||
|
|
||||||
'&::placeholder': {
|
'&::placeholder': {
|
||||||
fontStyle: 'italic',
|
fontStyle: 'italic',
|
||||||
color: theme.colors.grey,
|
color: theme.colors.grey,
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ interface FormCollectionProps {
|
|||||||
disableHomeLink?: boolean;
|
disableHomeLink?: boolean;
|
||||||
data: FormCollectionData;
|
data: FormCollectionData;
|
||||||
errors?: Record<string, Array<string>>;
|
errors?: Record<string, Array<string>>;
|
||||||
|
disableInputs?: boolean;
|
||||||
|
submitBtnDanger?: boolean;
|
||||||
|
|
||||||
setData: (name: string, value: any) => void;
|
setData: (name: string, value: any) => void;
|
||||||
handleSubmit: () => void;
|
handleSubmit: () => void;
|
||||||
@@ -29,6 +31,8 @@ export default function FormCollection({
|
|||||||
disableHomeLink,
|
disableHomeLink,
|
||||||
data,
|
data,
|
||||||
errors,
|
errors,
|
||||||
|
disableInputs = false,
|
||||||
|
submitBtnDanger = false,
|
||||||
|
|
||||||
setData,
|
setData,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -48,6 +52,7 @@ export default function FormCollection({
|
|||||||
handleSubmit={onSubmit}
|
handleSubmit={onSubmit}
|
||||||
canSubmit={canSubmit}
|
canSubmit={canSubmit}
|
||||||
disableHomeLink={disableHomeLink}
|
disableHomeLink={disableHomeLink}
|
||||||
|
submitBtnDanger={submitBtnDanger}
|
||||||
>
|
>
|
||||||
<BackToDashboard>
|
<BackToDashboard>
|
||||||
<TextBox
|
<TextBox
|
||||||
@@ -59,6 +64,7 @@ export default function FormCollection({
|
|||||||
errors={errors?.name}
|
errors={errors?.name}
|
||||||
required
|
required
|
||||||
autoFocus
|
autoFocus
|
||||||
|
disabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
<TextBox
|
<TextBox
|
||||||
label={t('collection.description')}
|
label={t('collection.description')}
|
||||||
@@ -67,12 +73,14 @@ export default function FormCollection({
|
|||||||
onChange={setData}
|
onChange={setData}
|
||||||
value={data.description ?? undefined}
|
value={data.description ?? undefined}
|
||||||
errors={errors?.description}
|
errors={errors?.description}
|
||||||
|
disabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Public"
|
label="Public"
|
||||||
name="visibility"
|
name="visibility"
|
||||||
onChange={handleOnCheck}
|
onChange={handleOnCheck}
|
||||||
checked={data.visibility === Visibility.PUBLIC}
|
checked={data.visibility === Visibility.PUBLIC}
|
||||||
|
disabled={disableInputs}
|
||||||
/>
|
/>
|
||||||
</BackToDashboard>
|
</BackToDashboard>
|
||||||
</FormLayout>
|
</FormLayout>
|
||||||
|
|||||||
@@ -30,16 +30,20 @@ interface FormLayoutProps {
|
|||||||
textSubmitButton?: string;
|
textSubmitButton?: string;
|
||||||
|
|
||||||
disableHomeLink?: boolean;
|
disableHomeLink?: boolean;
|
||||||
|
submitBtnDanger?: boolean;
|
||||||
collectionId?: string;
|
collectionId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function FormLayout({
|
export default function FormLayout({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
|
|
||||||
canSubmit,
|
canSubmit,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
textSubmitButton = i18n.t('common:confirm'),
|
textSubmitButton = i18n.t('common:confirm'),
|
||||||
|
|
||||||
disableHomeLink = false,
|
disableHomeLink = false,
|
||||||
|
submitBtnDanger = false,
|
||||||
collectionId,
|
collectionId,
|
||||||
}: FormLayoutProps) {
|
}: FormLayoutProps) {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
@@ -50,7 +54,7 @@ export default function FormLayout({
|
|||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
<Form onSubmit={handleSubmit}>
|
<Form onSubmit={handleSubmit}>
|
||||||
{children}
|
{children}
|
||||||
<Button type="submit" disabled={!canSubmit}>
|
<Button type="submit" disabled={!canSubmit} danger={submitBtnDanger}>
|
||||||
{textSubmitButton}
|
{textSubmitButton}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
41
inertia/pages/collections/delete.tsx
Normal file
41
inertia/pages/collections/delete.tsx
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import type Collection from '#models/collection';
|
||||||
|
import { useForm } from '@inertiajs/react';
|
||||||
|
import { route } from '@izzyjs/route/client';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import FormCollection, {
|
||||||
|
FormCollectionData,
|
||||||
|
} from '~/components/form/form_collection';
|
||||||
|
|
||||||
|
export default function DeleteCollectionPage({
|
||||||
|
collection,
|
||||||
|
}: {
|
||||||
|
collection: Collection;
|
||||||
|
}) {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
const { data, setData, submit, processing, errors } =
|
||||||
|
useForm<FormCollectionData>({
|
||||||
|
name: collection.name,
|
||||||
|
description: collection.description,
|
||||||
|
visibility: collection.visibility,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSubmit = () => {
|
||||||
|
const { method, url } = route('collection.delete', {
|
||||||
|
params: { id: collection.id },
|
||||||
|
});
|
||||||
|
submit(method, url);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormCollection
|
||||||
|
title={t('collection.delete')}
|
||||||
|
canSubmit={!processing}
|
||||||
|
data={data}
|
||||||
|
setData={setData}
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
errors={errors as any}
|
||||||
|
disableInputs={true}
|
||||||
|
submitBtnDanger
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import type Collection from '#models/collection';
|
|||||||
import { useForm } from '@inertiajs/react';
|
import { useForm } from '@inertiajs/react';
|
||||||
import { route } from '@izzyjs/route/client';
|
import { route } from '@izzyjs/route/client';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import FormCollection, {
|
import FormCollection, {
|
||||||
FormCollectionData,
|
FormCollectionData,
|
||||||
} from '~/components/form/form_collection';
|
} from '~/components/form/form_collection';
|
||||||
@@ -11,6 +12,7 @@ export default function EditCollectionPage({
|
|||||||
}: {
|
}: {
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
}) {
|
}) {
|
||||||
|
const { t } = useTranslation('common');
|
||||||
const { data, setData, submit, processing, errors } =
|
const { data, setData, submit, processing, errors } =
|
||||||
useForm<FormCollectionData>({
|
useForm<FormCollectionData>({
|
||||||
name: collection.name,
|
name: collection.name,
|
||||||
@@ -35,7 +37,7 @@ export default function EditCollectionPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormCollection
|
<FormCollection
|
||||||
title="Edit a collection"
|
title={t('collection.edit')}
|
||||||
canSubmit={canSubmit}
|
canSubmit={canSubmit}
|
||||||
data={data}
|
data={data}
|
||||||
setData={setData}
|
setData={setData}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function EditLinkPage({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormLink
|
<FormLink
|
||||||
title={t('link.create')}
|
title={t('link.edit')}
|
||||||
canSubmit={canSubmit}
|
canSubmit={canSubmit}
|
||||||
data={data}
|
data={data}
|
||||||
setData={setData}
|
setData={setData}
|
||||||
|
|||||||
@@ -26,7 +26,12 @@ router
|
|||||||
.put('/:id', [CollectionsController, 'update'])
|
.put('/:id', [CollectionsController, 'update'])
|
||||||
.as('collection.edit');
|
.as('collection.edit');
|
||||||
|
|
||||||
router.get('/delete', () => 'delete').as('collection.delete-form');
|
router
|
||||||
|
.get('/delete', [CollectionsController, 'showDeletePage'])
|
||||||
|
.as('collection.delete-form');
|
||||||
|
router
|
||||||
|
.delete('/:id', [CollectionsController, 'delete'])
|
||||||
|
.as('collection.delete');
|
||||||
})
|
})
|
||||||
.prefix('/collections');
|
.prefix('/collections');
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user