Skip to content

Commit

Permalink
feat(buildteams): ✨ Showcase Image Editing
Browse files Browse the repository at this point in the history
  • Loading branch information
Nudelsuppe42 committed Oct 13, 2023
1 parent c62e42c commit 7b66f7e
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 43 deletions.
32 changes: 32 additions & 0 deletions src/components/Dropzone.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { DropzoneProps, IMAGE_MIME_TYPE, Dropzone as MDropzone } from '@mantine/dropzone';
import { Group, Image, Text, rem } from '@mantine/core';
import { IconPhoto, IconUpload, IconX } from '@tabler/icons-react';

interface ExtendDropzone extends DropzoneProps {}

export function Dropzone(props: ExtendDropzone) {
return (
<MDropzone maxSize={3 * 1024 ** 2} accept={IMAGE_MIME_TYPE} {...props}>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<MDropzone.Accept>
<IconUpload style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-blue-6)' }} stroke={1.5} />
</MDropzone.Accept>
<MDropzone.Reject>
<IconX style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-red-6)' }} stroke={1.5} />
</MDropzone.Reject>
<MDropzone.Idle>
<IconPhoto style={{ width: rem(52), height: rem(52), color: 'var(--mantine-color-dimmed)' }} stroke={1.5} />
</MDropzone.Idle>

<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
File size should not exceed 5mb
</Text>
</div>
</Group>
</MDropzone>
);
}
5 changes: 3 additions & 2 deletions src/components/GalleryGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function GalleryGrid(props: GalleryGridProps) {
const isClient = useIsClient();

return (
<SimpleGrid spacing="md" cols={2 + (props.images.length % 2)}>
<SimpleGrid spacing="md" cols={2}>
{props.images.map((i, index) => (
<BackgroundImage
src={i.src}
Expand Down Expand Up @@ -64,11 +64,12 @@ function GalleryGrid(props: GalleryGridProps) {
textShadow: '0px 0px 28px #000',
userSelect: 'none',
}}
order={2}
>
{i.name}
</Title>
{i.date && (
<Badge variant="gradient" style={{ userSelect: 'none' }}>
<Badge variant="gradient" style={{ userSelect: 'none' }} size="sm">
{isClient ? new Date(i.date).toLocaleDateString() : ''}
</Badge>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/components/SettingsTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const SettingsTabs = ({ children, team, loading = false }: { children: any; team
<Tabs.Tab
value="images"
leftSection={<IconUsers size="0.8rem" />}
disabled={!user.hasPermission('team.socials.edit', team)}
disabled={!user.hasPermission('team.showcases.edit', team)}
>
Showcase Images
</Tabs.Tab>
Expand Down
2 changes: 2 additions & 0 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import '@mantine/notifications/styles.css';
import '@mantine/nprogress/styles.css';
import '@mantine/tiptap/styles.css';
import 'mapbox-gl/dist/mapbox-gl.css';
import '@mantine/dropzone/styles.css';
import '@mantine/dates/styles.css';
import '../styles/globals.css';

import type { AppProps } from 'next/app';
Expand Down
2 changes: 1 addition & 1 deletion src/pages/teams/[team]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const Team: NextPage = ({ data, data2 }: any) => {
<h2>{t('team.images')}</h2>
<GalleryGrid
images={
data2?.map((d: any) => ({
data2?.slice(0, 10).map((d: any) => ({
name: d?.title,
src: `https://cdn.buildtheearth.net/upload/${d?.image?.name}`,
date: d?.createdAt,
Expand Down
169 changes: 130 additions & 39 deletions src/pages/teams/[team]/manage/images.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,29 @@
import { ActionIcon, AspectRatio, Group, Table, Text, Tooltip, rem } from '@mantine/core';
import { IconPencil, IconTrash } from '@tabler/icons';
import {
ActionIcon,
AspectRatio,
Button,
FileInput,
Group,
Image,
Table,
Text,
TextInput,
Tooltip,
rem,
} from '@mantine/core';
import { IconPencil, IconPlus, IconTrash } from '@tabler/icons';
import useSWR, { mutate } from 'swr';

import { DateInput } from '@mantine/dates';
import { Dropzone } from '../../../../components/Dropzone';
import Page from '../../../../components/Page';
import SettingsTabs from '../../../../components/SettingsTabs';
import fetcher from '../../../../utils/Fetcher';
import { modals } from '@mantine/modals';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { showNotification } from '@mantine/notifications';
import { useRouter } from 'next/router';
import useSWR from 'swr';
import { useState } from 'react';
import { useUser } from '../../../../hooks/useUser';

var vagueTime = require('vague-time');
Expand All @@ -17,6 +32,7 @@ const Settings = () => {
const user = useUser();
const router = useRouter();
const { data } = useSWR(`/buildteams/${router.query.team}/showcases`);
const [loading, setLoading] = useState(false);

const handleDeleteImage = (image: any) => {
modals.openConfirmModal({
Expand All @@ -32,42 +48,113 @@ const Settings = () => {
confirmProps: { color: 'red' },
onCancel: () =>
showNotification({
message: `Showcase image was not deleted.`,
message: `Showcase Image was not deleted.`,
title: 'Cancelled Image removal',
color: 'yellow',
}),
onConfirm: () => {
// fetch(process.env.NEXT_PUBLIC_API_URL + `/buildteams/${router.query.team}/members`, {
// method: 'DELETE',
// headers: {
// 'Content-Type': 'application/json',
// Authorization: 'Bearer ' + user.token,
// },
// body: JSON.stringify({ user: member.id }),
// })
// .then((res) => res.json())
// .then((res) => {
// setHasUpdated(true);
// if (res.errors) {
// showNotification({
// title: 'Update failed',
// message: res.error,
// color: 'red',
// });
// } else {
// showNotification({
// title: 'Builder removed',
// message: 'All Data has been saved',
// color: 'green',
// });
// mutate(`/buildteams/${router.query.team}/members`);
// }
// });
fetch(process.env.NEXT_PUBLIC_API_URL + `/buildteams/${router.query.team}/showcases/${image.id}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + user.token,
},
})
.then((res) => res.json())
.then((res) => {
if (res.errors) {
showNotification({
title: 'Update failed',
message: res.error,
color: 'red',
});
} else {
showNotification({
title: 'Showcase Image removed',
message: 'All Data has been saved',
color: 'green',
});
mutate(`/buildteams/${router.query.team}/showcases`);
}
});
},
});
};
const handleEditImage = (image: any) => {};
const handleAddImage = () => {};
const handleAddImage = () => {
let image: any;
let name = '';
let date: Date | null;
modals.open({
id: 'add-image',
title: 'Add a new Showcase Image',
children: (
<>
<TextInput
label="Name"
description="The Name of the Showcase"
required
onChange={(e) => (name = e.target.value)}
/>
<FileInput
label="Image"
placeholder="Select an image..."
mt="md"
required
onChange={(e) => (image = e)}
accept="image/*"
/>
<DateInput defaultValue={new Date()} onChange={(e) => (date = e)} label="Date" mt="md" />
<Button
mt="md"
onClick={() => {
setLoading(true);
handleSubmit();
}}
loading={loading}
>
Add
</Button>
</>
),
centered: true,
});
const handleSubmit = () => {
setLoading(true);
const formdata = new FormData();
formdata.append('image', image);
formdata.append('title', name);
date && formdata.append('date', date.toISOString());

modals.closeAll();
fetch(process.env.NEXT_PUBLIC_API_URL + `/buildteams/${router.query.team}/showcases`, {
method: 'POST',
headers: {
// 'Content-Type': 'multipart/form-data',
Authorization: 'Bearer ' + user.token,
},
body: formdata,
})
.then((res) => res.json())
.then((res) => {
if (res.errors) {
showNotification({
title: 'Creation failed',
message: res.error,
color: 'red',
});
setLoading(false);
} else {
showNotification({
title: 'Showcase Image added',
message: 'All Data has been saved',
color: 'green',
});
mutate(`/buildteams/${router.query.team}/showcases`);
setLoading(false);
}
});
};
};

return (
<Page
Expand All @@ -78,14 +165,17 @@ const Settings = () => {
}}
seo={{ nofollow: true, noindex: true }}
>
<SettingsTabs team={router.query.team?.toString() || ''} loading={!data}>
<SettingsTabs team={router.query.team?.toString() || ''} loading={!data || loading}>
<Table.ScrollContainer minWidth={800}>
<Button leftSection={<IconPlus />} onClick={() => handleAddImage()} mb="md">
Add Showcase Image
</Button>
<Table verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>Title</Table.Th>
<Table.Th>Image</Table.Th>
<Table.Th>Created</Table.Th>
<Table.Th>Date</Table.Th>
<Table.Th></Table.Th>
</Table.Tr>
</Table.Thead>
Expand All @@ -99,15 +189,16 @@ const Settings = () => {
</AspectRatio>
</Table.Td>
<Table.Td>
<Tooltip withinPortal label={new Date(s.createdAt).toLocaleDateString()} position="top-start">
<p>{s.createdAt ? vagueTime.get({ to: new Date(s.createdAt) }) : ''}</p>
<Tooltip
withinPortal
label={s.createdAt ? vagueTime.get({ to: new Date(s.createdAt) }) : ''}
position="top-start"
>
<p>{new Date(s.createdAt).toLocaleDateString()} </p>
</Tooltip>
</Table.Td>
<Table.Td>
<Group gap={0} justify="flex-end">
<ActionIcon variant="subtle" color="gray" onClick={() => handleEditImage(s)}>
<IconPencil style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
</ActionIcon>
<ActionIcon variant="subtle" color="red" onClick={() => handleDeleteImage(s)}>
<IconTrash style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
</ActionIcon>
Expand Down

0 comments on commit 7b66f7e

Please sign in to comment.