From 7181f617ccb11c39fe902f76c80c7f3b7bc750d0 Mon Sep 17 00:00:00 2001 From: Nudesuppe42 Date: Wed, 16 Aug 2023 14:45:54 +0200 Subject: [PATCH] feat(buildteams): :sparkles: Settings Saving Settings Save Button, Reordered Settings Groups, Settings button on buildteam page only visible with permissions, SettingsTabs disabled for non-permitted pages --- src/components/Header.tsx | 22 +- src/components/RTE.tsx | 2 +- src/components/SWRProvider.tsx | 28 -- src/components/SWRSetup.tsx | 1 - src/components/SettingsTabs.tsx | 52 ++- src/hooks/useUser.ts | 19 +- src/pages/teams/[team]/manage/settings.tsx | 352 +++++++++++++-------- 7 files changed, 288 insertions(+), 188 deletions(-) delete mode 100644 src/components/SWRProvider.tsx diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 388e349e..90003a5f 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -29,6 +29,7 @@ import Icon from './Icon'; import { IconSettings } from '@tabler/icons'; import { useRouter } from 'next/router'; import { useTranslation } from 'react-i18next'; +import { useUser } from '../hooks/useUser'; const useStyles = createStyles((theme) => ({ root: { @@ -338,11 +339,13 @@ interface LogoHeaderProps { applyHref?: string; settingsHref?: string; invite?: string; + id: string; } export const LogoHeader = (props: LogoHeaderProps) => { const theme = useMantineTheme(); const { scrollY, scrollYProgress } = useScroll(); + const user = useUser(); const blur = useTransform(scrollYProgress, (latest) => `blur(${latest * 20}px)`); const bgPosY = useTransform(scrollYProgress, (latest) => `${latest * 20 + 50}%`); return ( @@ -443,10 +446,21 @@ export const LogoHeader = (props: LogoHeaderProps) => { Apply )} - + {user.hasPermissions( + [ + 'team.settings.edit', + 'team.socials.edit', + 'team.application.edit', + 'team.application.list', + 'team.application.review', + ], + props.id, + ) && ( + + )} diff --git a/src/components/RTE.tsx b/src/components/RTE.tsx index 5571d8a7..a11b8bce 100644 --- a/src/components/RTE.tsx +++ b/src/components/RTE.tsx @@ -43,7 +43,7 @@ export default function RTE({ }, [value]); return ( - + diff --git a/src/components/SWRProvider.tsx b/src/components/SWRProvider.tsx deleted file mode 100644 index 6a0c98b2..00000000 --- a/src/components/SWRProvider.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; -import { SWRConfig } from 'swr'; -import { useSession } from 'next-auth/react'; - -const SWRProvider = ({ children }: { children: React.ReactNode }) => { - const { data: session } = useSession(); - - return ( - - fetch(resource.startsWith('/') ? process.env.NEXT_PUBLIC_API_URL + resource : resource, { - ...init, - headers: { Authorization: session?.accessToken && `Bearer ${session?.accessToken}` }, - }).then((res) => res.json()), - shouldRetryOnError: false, - revalidateIfStale: false, - revalidateOnFocus: false, - revalidateOnReconnect: false, - }} - > - {children} - - ); -}; - -export default SWRProvider; diff --git a/src/components/SWRSetup.tsx b/src/components/SWRSetup.tsx index 953bb140..6e2fcee6 100644 --- a/src/components/SWRSetup.tsx +++ b/src/components/SWRSetup.tsx @@ -1,7 +1,6 @@ import { LoadingOverlay } from '@mantine/core'; import Page from './Page'; import { SWRConfig } from 'swr'; -import SWRProvider from './SWRProvider'; import { useSession } from 'next-auth/react'; export default function SWRSetup({ content }: any) { diff --git a/src/components/SettingsTabs.tsx b/src/components/SettingsTabs.tsx index dda9ab80..c1e5e93a 100644 --- a/src/components/SettingsTabs.tsx +++ b/src/components/SettingsTabs.tsx @@ -1,10 +1,14 @@ -import { Box, Tabs, useMantineTheme } from '@mantine/core'; +import { Box, LoadingOverlay, Tabs, useMantineTheme } from '@mantine/core'; import { IconDashboard, IconEyeglass, IconSearch, IconSend, IconSettings, IconUsers } from '@tabler/icons'; import { useRouter } from 'next/router'; +import { useSession } from 'next-auth/react'; +import { useUser } from '../hooks/useUser'; -const SettingsTabs = ({ children }: { children: any }) => { +const SettingsTabs = ({ children, team }: { children: any; team: string }) => { const theme = useMantineTheme(); + const user = useUser(); + const session = useSession(); const router = useRouter(); return ( <> @@ -12,29 +16,49 @@ const SettingsTabs = ({ children }: { children: any }) => { defaultValue="settings" variant="outline" value={router.pathname.split('/manage/')[1]} - onTabChange={(value) => router.push(`/teams/${router.query.team}/manage/${value}`)} + onTabChange={(value) => + value == 'quit' + ? router.push(`/teams/${router.query.team}`) + : router.push(`/teams/${router.query.team}/manage/${value}`) + } > - }> + } + disabled={!user.hasPermissions(['team.socials.edit', 'team.settings.edit'], team)} + > Settings - }> + } + disabled={!user.hasPermission('team.application.edit', team)} + > Application Questions - }> + } + disabled={!user.hasPermissions(['permission.add', 'permission.remove'], team)} + > Members - }> + } + disabled={!user.hasPermission('team.socials.edit', team)} + > Showcase Images - }> - Review - } - onClick={() => router.push(`/teams/${router.query.team}`)} + value="review" + icon={} + disabled={!user.hasPermissions(['team.application.review', 'team.application.list'], team)} > + Review + + }> Build Team Page @@ -45,8 +69,10 @@ const SettingsTabs = ({ children }: { children: any }) => { padding: `${theme.spacing.xs * 3}px`, paddingTop: theme.spacing.md, height: '100%', + position: 'relative', })} > + {children} diff --git a/src/hooks/useUser.ts b/src/hooks/useUser.ts index 63c0f4ee..b87f7f84 100644 --- a/src/hooks/useUser.ts +++ b/src/hooks/useUser.ts @@ -7,16 +7,31 @@ import { useSession } from 'next-auth/react'; export const useUser = () => { const { data } = useSWR('/account'); const session = useSession(); + const [loading, setLoading] = useState(true); + useEffect(() => { + if (data && session.status != 'loading') setLoading(false); + }, [data, session]); + const user = { user: data, token: session.data?.accessToken, + isLoading: loading, refresh: () => mutate('/account'), - hasPermission: (permission: string) => { + hasPermission: (p: string, buildteam?: string) => { + return user.hasPermissions([p], buildteam); + }, + hasPermissions: (ps: string[], buildteam?: string) => { const permissions = data?.permissions; if (!permissions) return false; - if (permissions.find((p: any) => minimatch(permission, p.permission))) return true; + if ( + permissions + .filter((p: any) => (buildteam ? p.buildTeamId === buildteam : true)) + .find((p: any) => ps.includes(p.permission)) + ) + return true; return false; }, + reload: () => mutate('/account'), }; return user; }; diff --git a/src/pages/teams/[team]/manage/settings.tsx b/src/pages/teams/[team]/manage/settings.tsx index 85daa0b7..1816e11c 100644 --- a/src/pages/teams/[team]/manage/settings.tsx +++ b/src/pages/teams/[team]/manage/settings.tsx @@ -5,29 +5,72 @@ import { Divider, Flex, Group, + Input, Select, Switch, TextInput, Textarea, useMantineTheme, } from '@mantine/core'; -import { IconPlus, IconTrash } from '@tabler/icons'; +import { IconCheck, IconPlus, IconTrash } from '@tabler/icons'; +import { useEffect, useState } from 'react'; import Page from '../../../../components/Page'; +import RTE from '../../../../components/RTE'; import SettingsTabs from '../../../../components/SettingsTabs'; import fetcher from '../../../../utils/Fetcher'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; +import { showNotification } from '@mantine/notifications'; import { useRouter } from 'next/router'; -import { useState } from 'react'; +import { useUser } from '../../../../hooks/useUser'; import { v4 as uuidv4 } from 'uuid'; const Settings = ({ data: tempData }: any) => { const theme = useMantineTheme(); const router = useRouter(); + const user = useUser(); const [data, setData] = useState(tempData); + const [allowSocial, setAllowSocial] = useState(false); + const [allowSettings, setAllowSettings] = useState(false); - const handleSave = () => { - console.log(data); + useEffect(() => { + if (!user.isLoading) { + setAllowSettings(user.hasPermission('team.settings.edit')); + setAllowSocial(user.hasPermission('team.socials.edit')); + } + }, [user]); + + const handleSave = (e: any) => { + e.preventDefault(); + const uploadingData = { ...data, socials: undefined, _count: undefined }; + console.log(uploadingData); + fetch(process.env.NEXT_PUBLIC_API_URL + `/buildteams/${uploadingData.id}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Bearer ' + user.token, + }, + body: JSON.stringify(uploadingData), + }) + .then((res) => res.json()) + .then((res) => { + if (res.errors) { + showNotification({ + title: 'Update failed', + message: res.error, + color: 'red', + }); + } else { + showNotification({ + title: 'Settings updated', + message: 'All Data has been saved', + color: 'green', + icon: , + }); + console.log(res); + setData({ ...data, ...res }); + } + }); }; const handleUpdate = (id: string, d: any) => { @@ -60,154 +103,185 @@ const Settings = ({ data: tempData }: any) => { }} seo={{ nofollow: true, noindex: true }} > - + {data && ( -
-

General Settings

- -
- handleUpdate('title', e.target.value)} - /> - handleUpdate('icon', e.target.value)} - /> - handleUpdate('backgroundImage', e.target.value)} - /> - handleUpdate('location', e.target.value)} - /> - + +

General Settings

+ +
+ handleUpdate('name', e.target.value)} + /> + handleUpdate('icon', e.target.value)} + /> + handleUpdate('backgroundImage', e.target.value)} + /> + handleUpdate('location', e.target.value)} + /> + handleUpdate('allowTrial', e.target.checked)} + /> +
+
+ + allowSettings && handleUpdate('about', e)} + /> + + handleUpdate('slug', e.target.value)} + /> +
+
+

Messages

+ +