From 75b0882f3e2479f022c7db6580e704a9f15584e6 Mon Sep 17 00:00:00 2001 From: eckoln Date: Wed, 26 Jun 2024 19:46:12 +0300 Subject: [PATCH 01/16] refactor(apps/web): add dynamic `platform` route --- .../[id]/commands/@tabs/custom/loading.tsx | 9 ++++ .../[id]/commands/@tabs/custom/page.tsx | 29 ++++++++++++ .../[id]/commands/@tabs/global/loading.tsx | 9 ++++ .../[id]/commands/@tabs/global/page.tsx | 29 ++++++++++++ .../[id]/commands/@tabs/layout.tsx} | 26 ++++++---- .../[platform]/[id]/commands/layout.tsx} | 12 +++-- .../dashboard/[platform]/[id]/page.tsx} | 34 ++++++++------ .../[id]/commands/@tabs/custom/page.tsx | 17 ------- .../[id]/commands/@tabs/global/page.tsx | 17 ------- .../discord/[id]/commands/@tabs/layout.tsx | 16 ------- .../discord/[id]/commands/layout.tsx | 9 ---- .../dashboard/discord/[id]/page.tsx | 17 ------- .../[id]/commands/@tabs/custom/page.tsx | 17 ------- .../[id]/commands/@tabs/global/page.tsx | 17 ------- .../twitch/[id]/commands/@tabs/layout.tsx | 16 ------- .../dashboard/twitch/[id]/commands/layout.tsx | 9 ---- .../dashboard/twitch/[id]/page.tsx | 17 ------- .../{commands.tsx => commands-list.tsx} | 2 +- .../pages/commands/commands-wrapper.tsx | 35 -------------- .../pages/overview/entity-logs-card.tsx | 47 +++++++++++++++++++ .../components/pages/overview/entity-logs.tsx | 43 ----------------- 21 files changed, 167 insertions(+), 260 deletions(-) create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/loading.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/loading.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx rename apps/web/src/{components/pages/commands/commands-tabs-layout.tsx => app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx} (59%) rename apps/web/src/{components/pages/commands/commands-layout.tsx => app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx} (58%) rename apps/web/src/{components/pages/overview/overview-view.tsx => app/(protected)/dashboard/[platform]/[id]/page.tsx} (53%) delete mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/custom/page.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/global/page.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/layout.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/commands/layout.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/page.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/custom/page.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/global/page.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/layout.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/layout.tsx delete mode 100644 apps/web/src/app/(protected)/dashboard/twitch/[id]/page.tsx rename apps/web/src/components/pages/commands/{commands.tsx => commands-list.tsx} (97%) delete mode 100644 apps/web/src/components/pages/commands/commands-wrapper.tsx create mode 100644 apps/web/src/components/pages/overview/entity-logs-card.tsx delete mode 100644 apps/web/src/components/pages/overview/entity-logs.tsx diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/loading.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/loading.tsx new file mode 100644 index 00000000..d1a8959d --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/loading.tsx @@ -0,0 +1,9 @@ +import { LoaderIcon } from '@/components/ui/icons' + +export default function Loading() { + return ( +
+ +
+ ) +} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx new file mode 100644 index 00000000..9116280c --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx @@ -0,0 +1,29 @@ +import type { Metadata } from 'next' +import { redirect } from 'next/navigation' + +import { CommandsList } from '@/components/pages/commands/commands-list' + +import { auth } from '@/lib/auth' + +export const metadata: Metadata = { + title: 'Custom Commands', +} + +interface Props { + params: { + platform: Platform + id: string + } +} + +export default async function Page({ params }: Props) { + const session = await auth() + + if (!session) { + redirect('/signin') + } + + return ( + + ) +} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/loading.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/loading.tsx new file mode 100644 index 00000000..d1a8959d --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/loading.tsx @@ -0,0 +1,9 @@ +import { LoaderIcon } from '@/components/ui/icons' + +export default function Loading() { + return ( +
+ +
+ ) +} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx new file mode 100644 index 00000000..197c6052 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx @@ -0,0 +1,29 @@ +import type { Metadata } from 'next' +import { redirect } from 'next/navigation' + +import { CommandsList } from '@/components/pages/commands/commands-list' + +import { auth } from '@/lib/auth' + +export const metadata: Metadata = { + title: 'Global Commands', +} + +interface Props { + params: { + platform: Platform + id: string + } +} + +export default async function Page({ params }: Props) { + const session = await auth() + + if (!session) { + redirect('/signin') + } + + return ( + + ) +} diff --git a/apps/web/src/components/pages/commands/commands-tabs-layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx similarity index 59% rename from apps/web/src/components/pages/commands/commands-tabs-layout.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx index 55bfb002..949ed2b5 100644 --- a/apps/web/src/components/pages/commands/commands-tabs-layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx @@ -1,24 +1,32 @@ import { TabGroup, TabGroupItem } from '@/components/ui/tab-group' +const tabs = [ + { + label: 'Custom Commands', + slug: 'custom', + }, + { + label: 'Global Commands', + slug: 'global', + }, +] + interface Props { - platform: Platform - id: string + params: { + platform: Platform + id: string + } children: React.ReactNode } -const tabs = [ - { label: 'Custom Commands', slug: 'custom' }, - { label: 'Global Commands', slug: 'global' }, -] - -export function CommandsTabsLayout({ platform, id, children }: Props) { +export default function TabsLayout({ params, children }: Props) { return (
{tabs.map((item) => { return ( diff --git a/apps/web/src/components/pages/commands/commands-layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx similarity index 58% rename from apps/web/src/components/pages/commands/commands-layout.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx index 658ca2b2..e716c90a 100644 --- a/apps/web/src/components/pages/commands/commands-layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx @@ -1,18 +1,20 @@ -import { CreateCommand } from './create-command' -import { ShareCommands } from './share-commands' +import { CreateCommand } from '@/components/pages/commands/create-command' +import { ShareCommands } from '@/components/pages/commands/share-commands' interface Props { + params: { + platform: Platform + } tabs: React.ReactNode - platform: Platform } -export function CommandsLayout({ tabs, platform }: Props) { +export default function Layout({ params, tabs }: Props) { return (

Commands

- +

Manage your commands.

diff --git a/apps/web/src/components/pages/overview/overview-view.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx similarity index 53% rename from apps/web/src/components/pages/overview/overview-view.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx index 06da98e2..0ce5e4fa 100644 --- a/apps/web/src/components/pages/overview/overview-view.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx @@ -1,20 +1,25 @@ import { Suspense } from 'react' +import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { EntityLogsCard } from '@/components/pages/overview/entity-logs-card' import { LoaderIcon } from '@/components/ui/icons' import { auth } from '@/lib/auth' -import { EntityLogs } from './entity-logs' +export const metadata: Metadata = { + title: 'Overview', +} interface Props { - platform: Platform - id: string + params: { + platform: Platform + id: string + } } -export async function OverviewView({ platform, id }: Props) { +export default async function Page({ params }: Props) { const session = await auth() if (!session) { @@ -30,16 +35,15 @@ export async function OverviewView({ platform, id }: Props) {

- - - Audit Logs - - - }> - - - - + + +
+ } + > + +
) diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/custom/page.tsx deleted file mode 100644 index 1c2c2f47..00000000 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/custom/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import CommandsWrapper from '@/components/pages/commands/commands-wrapper' - -export const metadata: Metadata = { - title: 'Custom Commands', -} - -interface Props { - params: { - id: string - } -} - -export default async function Page({ params }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/global/page.tsx deleted file mode 100644 index 9b216f99..00000000 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/global/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import CommandsWrapper from '@/components/pages/commands/commands-wrapper' - -export const metadata: Metadata = { - title: 'Global Commands', -} - -interface Props { - params: { - id: string - } -} - -export default async function Page({ params }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/layout.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/layout.tsx deleted file mode 100644 index 3a048e65..00000000 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/@tabs/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { CommandsTabsLayout } from '@/components/pages/commands/commands-tabs-layout' - -interface Props { - params: { - id: string - } - children: React.ReactNode -} - -export default function TabsLayout({ params, children }: Props) { - return ( - - {children} - - ) -} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/layout.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/layout.tsx deleted file mode 100644 index 6187488d..00000000 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/commands/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { CommandsLayout } from '@/components/pages/commands/commands-layout' - -interface Props { - tabs: React.ReactNode -} - -export default function Layout({ tabs }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/page.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/page.tsx deleted file mode 100644 index ce409412..00000000 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import { OverviewView } from '@/components/pages/overview/overview-view' - -export const metadata: Metadata = { - title: 'Overview', -} - -interface Props { - params: { - id: string - } -} - -export default function Page({ params }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/custom/page.tsx deleted file mode 100644 index 7f1ce141..00000000 --- a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/custom/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import CommandsWrapper from '@/components/pages/commands/commands-wrapper' - -export const metadata: Metadata = { - title: 'Custom Commands', -} - -interface Props { - params: { - id: string - } -} - -export default async function Page({ params }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/global/page.tsx deleted file mode 100644 index d6ef50db..00000000 --- a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/global/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import CommandsWrapper from '@/components/pages/commands/commands-wrapper' - -export const metadata: Metadata = { - title: 'Global Commands', -} - -interface Props { - params: { - id: string - } -} - -export default async function Page({ params }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/layout.tsx b/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/layout.tsx deleted file mode 100644 index 11ae78c7..00000000 --- a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/@tabs/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { CommandsTabsLayout } from '@/components/pages/commands/commands-tabs-layout' - -interface Props { - params: { - id: string - } - children: React.ReactNode -} - -export default function TabsLayout({ params, children }: Props) { - return ( - - {children} - - ) -} diff --git a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/layout.tsx b/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/layout.tsx deleted file mode 100644 index 90947e7d..00000000 --- a/apps/web/src/app/(protected)/dashboard/twitch/[id]/commands/layout.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { CommandsLayout } from '@/components/pages/commands/commands-layout' - -interface Props { - tabs: React.ReactNode -} - -export default function Layout({ tabs }: Props) { - return -} diff --git a/apps/web/src/app/(protected)/dashboard/twitch/[id]/page.tsx b/apps/web/src/app/(protected)/dashboard/twitch/[id]/page.tsx deleted file mode 100644 index 94e8dd41..00000000 --- a/apps/web/src/app/(protected)/dashboard/twitch/[id]/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import type { Metadata } from 'next' - -import { OverviewView } from '@/components/pages/overview/overview-view' - -export const metadata: Metadata = { - title: 'Overview', -} - -interface Props { - params: { - id: string - } -} - -export default function Page({ params }: Props) { - return -} diff --git a/apps/web/src/components/pages/commands/commands.tsx b/apps/web/src/components/pages/commands/commands-list.tsx similarity index 97% rename from apps/web/src/components/pages/commands/commands.tsx rename to apps/web/src/components/pages/commands/commands-list.tsx index ff180d27..c0ea9551 100644 --- a/apps/web/src/components/pages/commands/commands.tsx +++ b/apps/web/src/components/pages/commands/commands-list.tsx @@ -19,7 +19,7 @@ interface Props { type: CommandType } -export async function Commands({ platform, id, type }: Props) { +export async function CommandsList({ platform, id, type }: Props) { const commands = await getEntityCommands(platform, id, type) if (!commands.length) { diff --git a/apps/web/src/components/pages/commands/commands-wrapper.tsx b/apps/web/src/components/pages/commands/commands-wrapper.tsx deleted file mode 100644 index 93ae03ae..00000000 --- a/apps/web/src/components/pages/commands/commands-wrapper.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Suspense } from 'react' - -import { redirect } from 'next/navigation' - -import { Commands } from '@/components/pages/commands/commands' -import { LoaderIcon } from '@/components/ui/icons' - -import { auth } from '@/lib/auth' - -interface Props { - platform: Platform - id: string - type: CommandType -} - -export default async function CommandsWrapper({ platform, id, type }: Props) { - const session = await auth() - - if (!session) { - redirect('/signin') - } - - return ( - - - - } - key={Math.random()} - > - - - ) -} diff --git a/apps/web/src/components/pages/overview/entity-logs-card.tsx b/apps/web/src/components/pages/overview/entity-logs-card.tsx new file mode 100644 index 00000000..7173a284 --- /dev/null +++ b/apps/web/src/components/pages/overview/entity-logs-card.tsx @@ -0,0 +1,47 @@ +import { Badge } from '@/components/ui/badge' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' + +import { formatDate } from '@/lib/utils' + +import { getEntityLogs } from '@/data-layer/queries/entity' + +interface Props { + platform: Platform + id: string +} + +export async function EntityLogsCard({ platform, id }: Props) { + const logs = await getEntityLogs(platform, id) + + return ( + + + Audit Logs + + + {logs.length > 0 ? ( +
    + {logs + .map((item) => ( +
  • +
    + @{item.author} + {item.activity} +
    + + {formatDate(item.activity_date)} + +
  • + )) + .slice(0, 10)} +
+ ) : ( +

No logs found.

+ )} +
+
+ ) +} diff --git a/apps/web/src/components/pages/overview/entity-logs.tsx b/apps/web/src/components/pages/overview/entity-logs.tsx deleted file mode 100644 index 76534cda..00000000 --- a/apps/web/src/components/pages/overview/entity-logs.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { Badge } from '@/components/ui/badge' - -import { formatDate } from '@/lib/utils' - -import { getEntityLogs } from '@/data-layer/queries/entity' - -interface Props { - platform: Platform - platformEntityId: string -} - -export async function EntityLogs({ platform, platformEntityId }: Props) { - const logs = await getEntityLogs(platform, platformEntityId) - - /** - * IMPORTANT - * add here "activity logs are disabled" texts when added [platform]/settings page - */ - if (!logs.length) { - return

No logs found.

- } - - return ( -
    - {logs - .map((item) => ( -
  • -
    - @{item.author} - {item.activity} -
    - - {formatDate(item.activity_date)} - -
  • - )) - .slice(0, 10)} -
- ) -} From 53ca7e4b1281428fe689aa01feff9697de3be73a Mon Sep 17 00:00:00 2001 From: eckoln Date: Sat, 6 Jul 2024 11:37:13 +0300 Subject: [PATCH 02/16] refactor(apps/web)!: codebase --- apps/web/next.config.mjs | 2 +- apps/web/package.json | 3 +- apps/web/pnpm-lock.yaml | 12 + apps/web/src/app/(auth)/signin/page.tsx | 31 +- .../src/app/(auth)/signin/signin-button.tsx | 40 +++ .../web/src/app/(auth)/signin/signin-form.tsx | 69 +++++ apps/web/src/app/(landing)/page.tsx | 276 +++++++++++++++++- .../(landing)}/themed-image.tsx | 0 apps/web/src/app/(legal)/layout.tsx | 7 +- .../commands/@tabs/command-status-switch.tsx | 45 +++ .../[id]/commands/@tabs}/commands-list.tsx | 15 +- .../[id]/commands/@tabs/custom/page.tsx | 6 +- .../commands/@tabs/delete-command-button.tsx} | 29 +- .../[id]/commands/@tabs/global/page.tsx | 6 +- .../[platform]/[id]/commands/@tabs/layout.tsx | 2 + .../commands/@tabs/update-command-dialog.tsx | 120 ++++++++ .../[id]/commands/create-command-dialog.tsx | 120 ++++++++ .../[platform]/[id]/commands/layout.tsx | 9 +- .../[id]/commands/share-commands-button.tsx} | 0 .../[platform]/[id]}/entity-logs-card.tsx | 3 +- .../dashboard/[platform]/[id]/page.tsx | 7 +- .../dashboard/_sidebar}/bottom-nav.tsx | 0 .../_sidebar}/entities-dropdown-wrapper.tsx | 2 +- .../dashboard/_sidebar}/entities-dropdown.tsx | 2 + .../dashboard/_sidebar}/main-nav.tsx | 0 .../dashboard/_sidebar}/nav-link-item.tsx | 0 .../dashboard/_sidebar}/sidebar.tsx | 0 .../dashboard/_sidebar}/user-dropdown.tsx | 0 .../dashboard/joined-entities-list.tsx} | 4 +- .../src/app/(protected)/dashboard/layout.tsx | 5 +- .../src/app/(protected)/dashboard/page.tsx | 5 +- .../dashboard/settings/@tabs/layout.tsx | 2 +- .../@tabs/profile/link-account-button.tsx | 35 +++ .../@tabs/profile/linked-accounts-list.tsx} | 8 +- .../dashboard/settings/@tabs/profile/page.tsx | 5 +- .../@tabs/profile}/personal-information.tsx | 6 +- .../@tabs/servers/depart-entity-button.tsx} | 29 +- .../settings/@tabs/servers/entities-list.tsx} | 10 +- .../@tabs/servers/join-entity-button.tsx} | 42 ++- .../@tabs/servers/joinable-entities-list.tsx | 8 + .../@tabs/servers/joined-entities-list.tsx | 8 + .../dashboard/settings/@tabs/servers/page.tsx | 5 +- .../(protected)/dashboard/settings/layout.tsx | 2 +- apps/web/src/app/layout.tsx | 2 +- .../pages/commands/create-command-form.tsx | 98 ------- .../pages/commands/create-command.tsx | 56 ---- .../pages/commands/update-command-form.tsx | 96 ------ .../pages/commands/update-command-status.tsx | 50 ---- .../pages/commands/update-command.tsx | 41 --- .../src/components/pages/landing/features.tsx | 26 -- .../src/components/pages/landing/footer.tsx | 148 ---------- .../src/components/pages/landing/header.tsx | 59 ---- .../web/src/components/pages/landing/hero.tsx | 52 ---- .../pages/settings/joinable-entities.tsx | 8 - .../pages/settings/joined-entities.tsx | 8 - .../pages/settings/link-account.tsx | 38 --- .../components/pages/signin/signin-button.tsx | 53 ---- .../components/pages/signin/signin-error.tsx | 39 --- .../components/pages/signin/signin-form.tsx | 95 ------ .../{pages/dashboard => ui}/top-loader.tsx | 0 apps/web/src/{ => config}/env.ts | 0 apps/web/src/data-layer/actions/command.ts | 172 ----------- apps/web/src/data-layer/actions/entity.ts | 60 ---- apps/web/src/data-layer/queries/entity.ts | 49 ---- apps/web/src/data-layer/validations/index.ts | 3 - apps/web/src/lib/auth.ts | 8 +- apps/web/src/{data-layer => lib}/fetcher.ts | 2 +- apps/web/src/services/auth/actions.ts | 31 ++ apps/web/src/services/auth/schema.ts | 8 + apps/web/src/services/commands/actions.ts | 106 +++++++ apps/web/src/services/commands/queries.ts | 29 ++ .../commands/schema.ts} | 21 +- apps/web/src/services/commands/type.ts | 14 + apps/web/src/services/entities/actions.ts | 47 +++ apps/web/src/services/entities/queries.ts | 26 ++ apps/web/src/services/entities/schema.ts | 15 + apps/web/src/services/entities/type.ts | 11 + apps/web/src/services/shared/schema.ts | 3 + apps/web/src/services/shared/type.ts | 5 + .../user.ts => services/users/queries.ts} | 12 +- apps/web/src/services/users/type.ts | 19 ++ apps/web/src/types/index.ts | 43 --- 82 files changed, 1210 insertions(+), 1323 deletions(-) create mode 100644 apps/web/src/app/(auth)/signin/signin-button.tsx create mode 100644 apps/web/src/app/(auth)/signin/signin-form.tsx rename apps/web/src/{components/pages/landing => app/(landing)}/themed-image.tsx (100%) create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx rename apps/web/src/{components/pages/commands => app/(protected)/dashboard/[platform]/[id]/commands/@tabs}/commands-list.tsx (85%) rename apps/web/src/{components/pages/commands/delete-command.tsx => app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx} (57%) create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx rename apps/web/src/{components/pages/commands/share-commands.tsx => app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx} (100%) rename apps/web/src/{components/pages/overview => app/(protected)/dashboard/[platform]/[id]}/entity-logs-card.tsx (92%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/bottom-nav.tsx (100%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/entities-dropdown-wrapper.tsx (91%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/entities-dropdown.tsx (98%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/main-nav.tsx (100%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/nav-link-item.tsx (100%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/sidebar.tsx (100%) rename apps/web/src/{components/pages/dashboard => app/(protected)/dashboard/_sidebar}/user-dropdown.tsx (100%) rename apps/web/src/{components/pages/dashboard/joined-entities.tsx => app/(protected)/dashboard/joined-entities-list.tsx} (92%) create mode 100644 apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx rename apps/web/src/{components/pages/settings/linked-accounts.tsx => app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx} (92%) rename apps/web/src/{components/pages/settings => app/(protected)/dashboard/settings/@tabs/profile}/personal-information.tsx (86%) rename apps/web/src/{components/pages/settings/depart-entity.tsx => app/(protected)/dashboard/settings/@tabs/servers/depart-entity-button.tsx} (61%) rename apps/web/src/{components/pages/settings/entities.tsx => app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx} (84%) rename apps/web/src/{components/pages/settings/join-entity.tsx => app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx} (55%) create mode 100644 apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx delete mode 100644 apps/web/src/components/pages/commands/create-command-form.tsx delete mode 100644 apps/web/src/components/pages/commands/create-command.tsx delete mode 100644 apps/web/src/components/pages/commands/update-command-form.tsx delete mode 100644 apps/web/src/components/pages/commands/update-command-status.tsx delete mode 100644 apps/web/src/components/pages/commands/update-command.tsx delete mode 100644 apps/web/src/components/pages/landing/features.tsx delete mode 100644 apps/web/src/components/pages/landing/footer.tsx delete mode 100644 apps/web/src/components/pages/landing/header.tsx delete mode 100644 apps/web/src/components/pages/landing/hero.tsx delete mode 100644 apps/web/src/components/pages/settings/joinable-entities.tsx delete mode 100644 apps/web/src/components/pages/settings/joined-entities.tsx delete mode 100644 apps/web/src/components/pages/settings/link-account.tsx delete mode 100644 apps/web/src/components/pages/signin/signin-button.tsx delete mode 100644 apps/web/src/components/pages/signin/signin-error.tsx delete mode 100644 apps/web/src/components/pages/signin/signin-form.tsx rename apps/web/src/components/{pages/dashboard => ui}/top-loader.tsx (100%) rename apps/web/src/{ => config}/env.ts (100%) delete mode 100644 apps/web/src/data-layer/actions/command.ts delete mode 100644 apps/web/src/data-layer/actions/entity.ts delete mode 100644 apps/web/src/data-layer/queries/entity.ts delete mode 100644 apps/web/src/data-layer/validations/index.ts rename apps/web/src/{data-layer => lib}/fetcher.ts (97%) create mode 100644 apps/web/src/services/auth/actions.ts create mode 100644 apps/web/src/services/auth/schema.ts create mode 100644 apps/web/src/services/commands/actions.ts create mode 100644 apps/web/src/services/commands/queries.ts rename apps/web/src/{data-layer/validations/command.ts => services/commands/schema.ts} (50%) create mode 100644 apps/web/src/services/commands/type.ts create mode 100644 apps/web/src/services/entities/actions.ts create mode 100644 apps/web/src/services/entities/queries.ts create mode 100644 apps/web/src/services/entities/schema.ts create mode 100644 apps/web/src/services/entities/type.ts create mode 100644 apps/web/src/services/shared/schema.ts create mode 100644 apps/web/src/services/shared/type.ts rename apps/web/src/{data-layer/queries/user.ts => services/users/queries.ts} (62%) create mode 100644 apps/web/src/services/users/type.ts delete mode 100644 apps/web/src/types/index.ts diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 3b5073de..a5b9193f 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -4,7 +4,7 @@ import { fileURLToPath } from 'node:url' const jiti = createJiti(fileURLToPath(import.meta.url)) // Validate env during build. -jiti('./src/env') +jiti('./src/config/env') /** @type {import('next').NextConfig} */ const nextConfig = { diff --git a/apps/web/package.json b/apps/web/package.json index 626fbc9e..6488ba1f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -38,7 +38,8 @@ "sonner": "^1.4.41", "tailwind-merge": "^2.3.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.8" + "zod": "^3.23.8", + "zsa": "^0.5.0" }, "devDependencies": { "@trivago/prettier-plugin-sort-imports": "^4.3.0", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 29bd768e..a852fbb7 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: zod: specifier: ^3.23.8 version: 3.23.8 + zsa: + specifier: ^0.5.0 + version: 0.5.0(zod@3.23.8) devDependencies: '@trivago/prettier-plugin-sort-imports': specifier: ^4.3.0 @@ -2438,6 +2441,11 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zsa@0.5.0: + resolution: {integrity: sha512-SHh5cHjvi9uRnGhsHXUDYvYeSg8N7esgwrZVhIyGc5VR2UlI7S1mXsCGqO7nLIMOcDk4ULfB5SYLcmpai5auQw==} + peerDependencies: + zod: ^3.23.5 + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -4936,3 +4944,7 @@ snapshots: yocto-queue@0.1.0: {} zod@3.23.8: {} + + zsa@0.5.0(zod@3.23.8): + dependencies: + zod: 3.23.8 diff --git a/apps/web/src/app/(auth)/signin/page.tsx b/apps/web/src/app/(auth)/signin/page.tsx index a28f3b2e..45e3f985 100644 --- a/apps/web/src/app/(auth)/signin/page.tsx +++ b/apps/web/src/app/(auth)/signin/page.tsx @@ -2,21 +2,32 @@ import type { Metadata } from 'next' import Link from 'next/link' import { redirect } from 'next/navigation' -import { ArrowLeft } from 'lucide-react' +import { ArrowLeft, TriangleAlertIcon } from 'lucide-react' -import { SignInError } from '@/components/pages/signin/signin-error' -import { SignInForm } from '@/components/pages/signin/signin-form' +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' import { auth } from '@/lib/auth' +import { SignInForm } from './signin-form' + export const metadata: Metadata = { title: 'Sign in', } +type Error = 'OAuthAccountNotLinked' +function getErrorMessage(error: Error) { + switch (error) { + case 'OAuthAccountNotLinked': + return 'The account is already associated with another user.' + default: + return 'Something went wrong!' + } +} + interface Props { searchParams: { - error: string + error: Error } } @@ -28,6 +39,8 @@ export default async function Page({ searchParams }: Props) { redirect('/dashboard') } + const errorMessage = getErrorMessage(searchParams.error) + return (
+
{session ? (

@@ -52,7 +66,14 @@ export default async function Page({ searchParams }: Props) { )} - + + {searchParams.error && ( + + + Error + {errorMessage} + + )}

) diff --git a/apps/web/src/app/(auth)/signin/signin-button.tsx b/apps/web/src/app/(auth)/signin/signin-button.tsx new file mode 100644 index 00000000..7696719e --- /dev/null +++ b/apps/web/src/app/(auth)/signin/signin-button.tsx @@ -0,0 +1,40 @@ +'use client' + +import { useTransition } from 'react' + +import { Button } from '@/components/ui/button' +import { DiscordIcon, LoaderIcon, TwitchIcon } from '@/components/ui/icons' + +import { signInWithProvider } from '@/services/auth/actions' + +interface Props { + provider: 'twitch' | 'discord' + isDisabled?: boolean +} + +export function SignInButton({ provider, isDisabled = false }: Props) { + const [pending, startTrantision] = useTransition() + + return ( + + ) +} diff --git a/apps/web/src/app/(auth)/signin/signin-form.tsx b/apps/web/src/app/(auth)/signin/signin-form.tsx new file mode 100644 index 00000000..a3e8d5cb --- /dev/null +++ b/apps/web/src/app/(auth)/signin/signin-form.tsx @@ -0,0 +1,69 @@ +'use client' + +import { useState } from 'react' + +import { Checkbox } from '@/components/ui/checkbox' +import { Label } from '@/components/ui/label' +import { Link } from '@/components/ui/link' + +import { SignInButton } from './signin-button' + +const legalLinks = [ + { + label: 'Privacy Policy', + href: '/privacy-policy', + }, + { + label: 'Terms of Service', + href: '/terms-of-service', + }, + { + label: 'Cookie Policy', + href: '/cookie-policy', + }, + { + label: 'EULA', + href: '/eula', + }, +] + +export function SignInForm() { + const [checked, setChecked] = useState(false) + + return ( +
+
+ + +
+
+ setChecked((prev) => !prev)} + /> +
+ +

+ You agree to our{' '} + {legalLinks.map((item) => ( + + + {item.label} + + + ))} +

+
+
+
+ ) +} diff --git a/apps/web/src/app/(landing)/page.tsx b/apps/web/src/app/(landing)/page.tsx index 578cbd7f..2f745819 100644 --- a/apps/web/src/app/(landing)/page.tsx +++ b/apps/web/src/app/(landing)/page.tsx @@ -1,15 +1,275 @@ -import { Features } from '@/components/pages/landing/features' -import { Footer } from '@/components/pages/landing/footer' -import { Header } from '@/components/pages/landing/header' -import { Hero } from '@/components/pages/landing/hero' +import Image from 'next/image' +import NextLink from 'next/link' + +import { CheckCircleIcon, XIcon } from 'lucide-react' + +import { ThemeToggle } from '@/components/theme-toggle' +import { Button } from '@/components/ui/button' +import { GitHubIcon } from '@/components/ui/icons' +import { Link } from '@/components/ui/link' + +import { ThemedImage } from './themed-image' + +const navLinks = [ + { + label: 'Documentation', + path: '/docs', + options: { + target: '_blank', + rel: 'noreferrer', + }, + }, +] + +const productLinks = [ + { + label: 'Dashboard', + href: '/dashboard', + }, + { + label: 'Documentation', + href: '/docs', + options: { + target: '_blank', + rel: 'noreferrer', + }, + }, +] + +const legalLinks = [ + { + label: 'Terms of Service', + href: '/terms-of-service', + }, + { + label: 'Privacy Policy', + href: '/privacy-policy', + }, + { + label: 'Cookie Policy', + href: '/cookie-policy', + }, + { + label: 'EULA', + href: '/eula', + }, +] + +const socialLinks = [ + { + label: 'Twitter', + href: 'https://x.com/senchabot', + }, + { + label: 'Discord', + href: '/discord', + }, + { + label: 'GitHub', + href: '/github', + }, + { + label: 'LinkedIn', + href: 'https://www.linkedin.com/company/senchabot/', + }, +] export default function Page() { return (
-
- - -
+ {/* header */} +
+ +
+ + {/* hero */} +
+
+
+

+ Manage Your Community from One Place +

+

+ Multi-platform bot designed for seamless integration with Twitch + and Discord. +

+
+ +

+ We're open source on + + + GitHub + +

+
+
+
+ +
+
+
+ + {/* features */} +
+
+
    +
  • + +

    + Download required +

    +
  • +
  • + +

    Free

    +
  • +
  • + +

    Open-source

    +
  • +
+
+
+ + {/* footer */} +
+
+
+
+
+ Senchabot +
+ Senchabot +
+

+ Manage your Discord and Twitch community with an open-source + multi-platform bot. +

+
+
+
+ + Product + +
    + {productLinks.map((item) => ( +
  • + + {item.label} + +
  • + ))} +
+
+
+ + Legal + +
    + {legalLinks.map((item) => ( +
  • + + {item.label} + +
  • + ))} +
+
+
+ + Social + +
    + {socialLinks.map((item) => ( +
  • + + {item.label} + +
  • + ))} +
+
+
+
+
+
+

© {new Date().getFullYear()} Senchabot. All Rights Reserved.

+

+ Made with from the + community. +

+
+
+
) } diff --git a/apps/web/src/components/pages/landing/themed-image.tsx b/apps/web/src/app/(landing)/themed-image.tsx similarity index 100% rename from apps/web/src/components/pages/landing/themed-image.tsx rename to apps/web/src/app/(landing)/themed-image.tsx diff --git a/apps/web/src/app/(legal)/layout.tsx b/apps/web/src/app/(legal)/layout.tsx index 806115c2..72b9c13d 100644 --- a/apps/web/src/app/(legal)/layout.tsx +++ b/apps/web/src/app/(legal)/layout.tsx @@ -1,6 +1,3 @@ -import { Footer } from '@/components/pages/landing/footer' -import { Header } from '@/components/pages/landing/header' - interface Props { children: React.ReactNode } @@ -8,11 +5,9 @@ interface Props { export default function LegalLayout({ children }: Props) { return (
-
-
+
{children}
-
) } diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx new file mode 100644 index 00000000..69f3dc6f --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx @@ -0,0 +1,45 @@ +'use client' + +import { useTransition } from 'react' + +import { toast } from 'sonner' + +import { Switch } from '@/components/ui/switch' + +import { setCommandStatus } from '@/services/commands/actions' +import type { EntityCommand } from '@/services/commands/type' + +interface Props { + command: EntityCommand +} + +export function CommandStatusSwitch({ command }: Props) { + const [pending, startTransition] = useTransition() + + return ( +
+ { + startTransition(async () => { + const [, error] = await setCommandStatus({ + id: command.id, + platform: command.platform, + platformEntityId: command.platform_entity_id, + status: checked, + }) + + if (error) { + toast.error(error.message) + return + } + + toast.success('Successfully updated!') + }) + }} + /> +
+ ) +} diff --git a/apps/web/src/components/pages/commands/commands-list.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx similarity index 85% rename from apps/web/src/components/pages/commands/commands-list.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx index c0ea9551..b76d0692 100644 --- a/apps/web/src/components/pages/commands/commands-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx @@ -7,20 +7,21 @@ import { TableRow, } from '@/components/ui/table' -import { getEntityCommands } from '@/data-layer/queries/entity' +import { getCommands } from '@/services/commands/queries' +import type { Platform } from '@/services/shared/type' -import { DeleteCommand } from './delete-command' -import { UpdateCommand } from './update-command' -import { UpdateCommandStatus } from './update-command-status' +import { CommandStatusSwitch } from './command-status-switch' +import { DeleteCommand } from './delete-command-button' +import { UpdateCommand } from './update-command-dialog' interface Props { platform: Platform id: string - type: CommandType + type: 'custom' | 'global' } export async function CommandsList({ platform, id, type }: Props) { - const commands = await getEntityCommands(platform, id, type) + const commands = await getCommands(platform, id, type) if (!commands.length) { return

No command found.

@@ -42,7 +43,7 @@ export async function CommandsList({ platform, id, type }: Props) { {commands.map((item) => ( - + !{item.name} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx index 9116280c..f685260b 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx @@ -1,10 +1,12 @@ import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { CommandsList } from '@/components/pages/commands/commands-list' - import { auth } from '@/lib/auth' +import type { Platform } from '@/services/shared/type' + +import { CommandsList } from '../commands-list' + export const metadata: Metadata = { title: 'Custom Commands', } diff --git a/apps/web/src/components/pages/commands/delete-command.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx similarity index 57% rename from apps/web/src/components/pages/commands/delete-command.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx index d16af695..3aa36356 100644 --- a/apps/web/src/components/pages/commands/delete-command.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx @@ -7,7 +7,8 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { deleteEntityCommand } from '@/data-layer/actions/command' +import { deleteCommand } from '@/services/commands/actions' +import type { Platform } from '@/services/shared/type' interface Props { id: number @@ -26,21 +27,19 @@ export function DeleteCommand({ id, platform, platformEntityId }: Props) { onClick={() => { if (!confirm('Are you sure you want to perform this action?')) return - startTransition(() => { - const dispatch = deleteEntityCommand(id, platform, platformEntityId) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - return message - }, - error: ({ message }) => { - return message - }, + startTransition(async () => { + const [, error] = await deleteCommand({ + id, + platform, + platformEntityId, }) + + if (error) { + toast.error(error.message) + return + } + + toast.success('Successfully deleted!') }) }} > diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx index 197c6052..ba89e4fb 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx @@ -1,10 +1,12 @@ import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { CommandsList } from '@/components/pages/commands/commands-list' - import { auth } from '@/lib/auth' +import type { Platform } from '@/services/shared/type' + +import { CommandsList } from '../commands-list' + export const metadata: Metadata = { title: 'Global Commands', } diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx index 949ed2b5..f45d1770 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx @@ -1,5 +1,7 @@ import { TabGroup, TabGroupItem } from '@/components/ui/tab-group' +import type { Platform } from '@/services/shared/type' + const tabs = [ { label: 'Custom Commands', diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx new file mode 100644 index 00000000..432a729a --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx @@ -0,0 +1,120 @@ +'use client' + +import { useState } from 'react' +import { useFormStatus } from 'react-dom' + +import { toast } from 'sonner' + +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' +import { LoaderIcon } from '@/components/ui/icons' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Link } from '@/components/ui/link' +import { Switch } from '@/components/ui/switch' + +import { updateCommand } from '@/services/commands/actions' +import type { EntityCommand } from '@/services/commands/type' + +interface Props { + command: EntityCommand +} + +export function UpdateCommand({ command }: Props) { + const [open, setOpen] = useState(false) + + return ( + + + + + + + Update Command + +
{ + // set platform fields + formData.append('id', String(command.id)) + formData.append('platform', command.platform) + formData.append('platformEntityId', command.platform_entity_id) + + const [, error] = await updateCommand(formData) + + if (error) { + if (error.code === 'INPUT_PARSE_ERROR') { + toast.error('Invalid submission!') + return + } else { + toast.error(error.message) + return + } + } + + toast.success('Successfully updated!') + }} + > +
+
+ + +
+
+ + +

+ See our{' '} + + docs + {' '} + for more variables. +

+
+
+ + +
+
+ + +
+
+ ) +} + +function SaveButton() { + const { pending } = useFormStatus() + return ( +
+ +
+ ) +} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx new file mode 100644 index 00000000..1fce90e7 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx @@ -0,0 +1,120 @@ +'use client' + +import { useRef, useState } from 'react' +import { useFormStatus } from 'react-dom' + +import { PlusIcon } from 'lucide-react' +import { toast } from 'sonner' + +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog' +import { LoaderIcon } from '@/components/ui/icons' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Link } from '@/components/ui/link' +import { Switch } from '@/components/ui/switch' + +import { createCommand } from '@/services/commands/actions' +import type { Platform } from '@/services/shared/type' + +interface Props { + platform: Platform + entityId: string +} + +export function CreateCommand({ platform, entityId }: Props) { + const ref = useRef(null) + const [open, setOpen] = useState(false) + + return ( + + + + + + + Create Command + +
{ + // set platform field + formData.append('platform', platform) + formData.append('platformEntityId', entityId) + + const [, error] = await createCommand(formData) + + if (error) { + if (error.code === 'INPUT_PARSE_ERROR') { + toast.error('Invalid submission!') + return + } else { + toast.error(error.message) + return + } + } + + ref.current?.reset() + setOpen(false) + toast.success('Successfully added!') + }} + > +
+
+ + +
+
+ + +

+ See our{' '} + + docs + {' '} + for more variables. +

+
+
+ + +
+
+ + +
+
+ ) +} + +function SubmitButton() { + const { pending } = useFormStatus() + return ( +
+ +
+ ) +} diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx index e716c90a..15aa1825 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx @@ -1,9 +1,12 @@ -import { CreateCommand } from '@/components/pages/commands/create-command' -import { ShareCommands } from '@/components/pages/commands/share-commands' +import type { Platform } from '@/services/shared/type' + +import { CreateCommand } from './create-command-dialog' +import { ShareCommands } from './share-commands-button' interface Props { params: { platform: Platform + id: string } tabs: React.ReactNode } @@ -14,7 +17,7 @@ export default function Layout({ params, tabs }: Props) {

Commands

- +

Manage your commands.

diff --git a/apps/web/src/components/pages/commands/share-commands.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx similarity index 100% rename from apps/web/src/components/pages/commands/share-commands.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx diff --git a/apps/web/src/components/pages/overview/entity-logs-card.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx similarity index 92% rename from apps/web/src/components/pages/overview/entity-logs-card.tsx rename to apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx index 7173a284..0d6d95cb 100644 --- a/apps/web/src/components/pages/overview/entity-logs-card.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx @@ -3,7 +3,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { formatDate } from '@/lib/utils' -import { getEntityLogs } from '@/data-layer/queries/entity' +import { getEntityLogs } from '@/services/entities/queries' +import type { Platform } from '@/services/shared/type' interface Props { platform: Platform diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx index 0ce5e4fa..86b1b091 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx @@ -3,11 +3,14 @@ import { Suspense } from 'react' import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { EntityLogsCard } from '@/components/pages/overview/entity-logs-card' import { LoaderIcon } from '@/components/ui/icons' import { auth } from '@/lib/auth' +import type { Platform } from '@/services/shared/type' + +import { EntityLogsCard } from './entity-logs-card' + export const metadata: Metadata = { title: 'Overview', } @@ -34,7 +37,7 @@ export default async function Page({ params }: Props) { Overview of your server or channel.

-
+
diff --git a/apps/web/src/components/pages/dashboard/bottom-nav.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/bottom-nav.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/bottom-nav.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/bottom-nav.tsx diff --git a/apps/web/src/components/pages/dashboard/entities-dropdown-wrapper.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx similarity index 91% rename from apps/web/src/components/pages/dashboard/entities-dropdown-wrapper.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx index abced6ac..8765455c 100644 --- a/apps/web/src/components/pages/dashboard/entities-dropdown-wrapper.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx @@ -4,7 +4,7 @@ import { PlusIcon } from 'lucide-react' import { Button } from '@/components/ui/button' -import { getUserEntities } from '@/data-layer/queries/user' +import { getUserEntities } from '@/services/users/queries' import { EntitiesDropdown } from './entities-dropdown' diff --git a/apps/web/src/components/pages/dashboard/entities-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx similarity index 98% rename from apps/web/src/components/pages/dashboard/entities-dropdown.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx index dffcdcc6..24b799b4 100644 --- a/apps/web/src/components/pages/dashboard/entities-dropdown.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx @@ -27,6 +27,8 @@ import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' +import type { UserEntity } from '@/services/users/type' + interface Props { entities: UserEntity[] } diff --git a/apps/web/src/components/pages/dashboard/main-nav.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/main-nav.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx diff --git a/apps/web/src/components/pages/dashboard/nav-link-item.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/nav-link-item.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/nav-link-item.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/nav-link-item.tsx diff --git a/apps/web/src/components/pages/dashboard/sidebar.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/sidebar.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/sidebar.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/sidebar.tsx diff --git a/apps/web/src/components/pages/dashboard/user-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/user-dropdown.tsx rename to apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx diff --git a/apps/web/src/components/pages/dashboard/joined-entities.tsx b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx similarity index 92% rename from apps/web/src/components/pages/dashboard/joined-entities.tsx rename to apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx index b3076084..b2361c70 100644 --- a/apps/web/src/components/pages/dashboard/joined-entities.tsx +++ b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx @@ -3,9 +3,9 @@ import NextLink from 'next/link' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Link } from '@/components/ui/link' -import { getUserEntities } from '@/data-layer/queries/user' +import { getUserEntities } from '@/services/users/queries' -export async function JoinedEntities() { +export async function JoinedEntitiesList() { const entities = await getUserEntities('joined') if (!entities.length) { diff --git a/apps/web/src/app/(protected)/dashboard/layout.tsx b/apps/web/src/app/(protected)/dashboard/layout.tsx index d4c2aa6c..818f4a4b 100644 --- a/apps/web/src/app/(protected)/dashboard/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/layout.tsx @@ -1,6 +1,7 @@ -import { Sidebar } from '@/components/pages/dashboard/sidebar' -import { TopLoader } from '@/components/pages/dashboard/top-loader' import { Toaster } from '@/components/ui/sonner' +import { TopLoader } from '@/components/ui/top-loader' + +import { Sidebar } from './_sidebar/sidebar' interface Props { children: React.ReactNode diff --git a/apps/web/src/app/(protected)/dashboard/page.tsx b/apps/web/src/app/(protected)/dashboard/page.tsx index 33b36d4d..d6f0fd3b 100644 --- a/apps/web/src/app/(protected)/dashboard/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/page.tsx @@ -3,11 +3,12 @@ import { Suspense } from 'react' import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { JoinedEntities } from '@/components/pages/dashboard/joined-entities' import { LoaderIcon } from '@/components/ui/icons' import { auth } from '@/lib/auth' +import { JoinedEntitiesList } from './joined-entities-list' + export const metadata: Metadata = { title: 'Dashboard', } @@ -30,7 +31,7 @@ export default async function Page() {

My Servers & Channels

}> - +
diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/layout.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/layout.tsx index a084e24c..12a0c003 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/layout.tsx @@ -4,7 +4,7 @@ interface Props { children: React.ReactNode } -export default function TabsLayout({ children }: Props) { +export default function Layout({ children }: Props) { return (
diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx new file mode 100644 index 00000000..c4c992a3 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx @@ -0,0 +1,35 @@ +'use client' + +import { useTransition } from 'react' + +import { Button } from '@/components/ui/button' +import { LoaderIcon } from '@/components/ui/icons' + +import { signInWithProvider } from '@/services/auth/actions' + +interface Props { + provider: 'twitch' | 'discord' +} + +export function LinkAccount({ provider }: Props) { + const [pending, startTransition] = useTransition() + + return ( + + ) +} diff --git a/apps/web/src/components/pages/settings/linked-accounts.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx similarity index 92% rename from apps/web/src/components/pages/settings/linked-accounts.tsx rename to apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx index be7298e7..b3b6ff12 100644 --- a/apps/web/src/components/pages/settings/linked-accounts.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx @@ -4,9 +4,9 @@ import { DiscordIcon, TwitchIcon } from '@/components/ui/icons' import { formatDate } from '@/lib/utils' -import { getUserAccounts } from '@/data-layer/queries/user' +import { getUserAccounts } from '@/services/users/queries' -import { LinkAccount } from './link-account' +import { LinkAccount } from './link-account-button' export async function LinkedAccounts() { const accounts = await getUserAccounts() @@ -27,7 +27,7 @@ export async function LinkedAccounts() { {formatDate(findTwitchAcc.created_at)} ) : ( - + )}
@@ -56,7 +56,7 @@ export async function LinkedAccounts() { {formatDate(findDiscordAcc.created_at)} ) : ( - + )}
diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx index 98827e7f..a5e0e408 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx @@ -3,8 +3,6 @@ import { Suspense } from 'react' import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { LinkedAccounts } from '@/components/pages/settings/linked-accounts' -import { PersonalInformation } from '@/components/pages/settings/personal-information' import { Card, CardContent, @@ -16,6 +14,9 @@ import { LoaderIcon } from '@/components/ui/icons' import { auth } from '@/lib/auth' +import { LinkedAccounts } from './linked-accounts-list' +import { PersonalInformation } from './personal-information' + export const metadata: Metadata = { title: 'Profile', } diff --git a/apps/web/src/components/pages/settings/personal-information.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx similarity index 86% rename from apps/web/src/components/pages/settings/personal-information.tsx rename to apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx index 9197df83..2fa3a003 100644 --- a/apps/web/src/components/pages/settings/personal-information.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx @@ -12,12 +12,12 @@ export async function PersonalInformation() { } return ( -
-
+
+
-
+
{ if (!confirm('Are you sure you want to perform this action?')) return - startTransition(() => { - const dispatch = executeEntityAction( - 'depart', + startTransition(async () => { + const [, error] = await executeEntityAction({ platform, platformEntityId, - ) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - return message - }, - error: ({ message }) => { - return message - }, + action: 'depart', }) + + if (error) { + toast.error(error.message) + return + } + + toast.success('Successfully departed!') }) }} > diff --git a/apps/web/src/components/pages/settings/entities.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx similarity index 84% rename from apps/web/src/components/pages/settings/entities.tsx rename to apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx index 66784f5a..1b04b8fc 100644 --- a/apps/web/src/components/pages/settings/entities.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx @@ -1,15 +1,17 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import { env } from '@/env' +import type { UserEntity } from '@/services/users/type' -import { DepartEntity } from './depart-entity' -import { JoinEntity } from './join-entity' +import { env } from '@/config/env' + +import { DepartEntity } from './depart-entity-button' +import { JoinEntity } from './join-entity-button' interface Props { entities: UserEntity[] } -export function Entities({ entities }: Props) { +export function EntitiesList({ entities }: Props) { const DISCORD_CLIENT_ID = env.AUTH_DISCORD_ID return ( diff --git a/apps/web/src/components/pages/settings/join-entity.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx similarity index 55% rename from apps/web/src/components/pages/settings/join-entity.tsx rename to apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx index 334fecf4..00bf10ec 100644 --- a/apps/web/src/components/pages/settings/join-entity.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx @@ -8,7 +8,8 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { executeEntityAction } from '@/data-layer/actions/entity' +import { executeEntityAction } from '@/services/entities/actions' +import type { Platform } from '@/services/shared/type' interface Props { platform: Platform @@ -29,35 +30,28 @@ export function JoinEntity({ size="sm" disabled={pending} onClick={() => { - startTransition(() => { + startTransition(async () => { if (platform === 'twitch') { - const dispatch = executeEntityAction( - 'join', + const [, error] = await executeEntityAction({ platform, platformEntityId, - ) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - return message - }, - error: ({ message }) => { - return message - }, + action: 'join', }) + + if (error) { + toast.error(error.message) + return + } + + toast.success('Successfully joined!') } else if (platform === 'discord') { const BASE_URL = 'https://discord.com/oauth2/authorize?' - const params = new URLSearchParams({ - client_id: discordClientId, - guild_id: platformEntityId, - disable_guild_select: 'true', - permission: '2199022698327', - scope: ['bot', 'applications.commands'].join(' '), - }) + const params = new URLSearchParams() + params.append('client_id', discordClientId) + params.append('guild_id', platformEntityId) + params.append('disable_guild_select', 'true') + params.append('permission', '2199022698327') + params.append('scope', 'applications.commands') window.open(new URL(BASE_URL + params), '_blank', 'noreferrer') } diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx new file mode 100644 index 00000000..52ba2417 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx @@ -0,0 +1,8 @@ +import { getUserEntities } from '@/services/users/queries' + +import { EntitiesList } from './entities-list' + +export async function JoinableEntities() { + const entities = await getUserEntities('not_joined') + return +} diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx new file mode 100644 index 00000000..4663c8f2 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx @@ -0,0 +1,8 @@ +import { getUserEntities } from '@/services/users/queries' + +import { EntitiesList } from './entities-list' + +export async function JoinedEntities() { + const entities = await getUserEntities('joined') + return +} diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx index c89bd97f..d4f65a14 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx @@ -3,8 +3,6 @@ import { Suspense } from 'react' import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { JoinableEntities } from '@/components/pages/settings/joinable-entities' -import { JoinedEntities } from '@/components/pages/settings/joined-entities' import { Card, CardContent, @@ -16,6 +14,9 @@ import { LoaderIcon } from '@/components/ui/icons' import { auth } from '@/lib/auth' +import { JoinableEntities } from './joinable-entities-list' +import { JoinedEntities } from './joined-entities-list' + export const metadata: Metadata = { title: 'Servers & Channels', } diff --git a/apps/web/src/app/(protected)/dashboard/settings/layout.tsx b/apps/web/src/app/(protected)/dashboard/settings/layout.tsx index a2f35ac0..4e8f300d 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/layout.tsx @@ -2,7 +2,7 @@ interface Props { tabs: React.ReactNode } -export default function SettingsLayout({ tabs }: Props) { +export default function Layout({ tabs }: Props) { return (
diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index 66cc17ef..9f72b925 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -36,7 +36,7 @@ interface Props { children: React.ReactNode } -export default function RootLayout({ children }: Props) { +export default function Layout({ children }: Props) { return ( void -} - -export function CreateCommandForm({ - platform, - platformEntityId, - afterSubmission, -}: Props) { - return ( -
{ - // set platform fields - formData.append('platform', platform) - formData.append('platformEntityId', platformEntityId) - - const dispatch = createEntityCommand(formData) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - afterSubmission() - return message - }, - error: ({ message }) => { - return message - }, - }) - }} - > -
-
- - -
-
- - -

- See our{' '} - - docs - {' '} - for more variables. -

-
-
- - -
-
- - - ) -} - -function SubmitButton() { - const { pending } = useFormStatus() - return ( -
- -
- ) -} diff --git a/apps/web/src/components/pages/commands/create-command.tsx b/apps/web/src/components/pages/commands/create-command.tsx deleted file mode 100644 index 9ed61048..00000000 --- a/apps/web/src/components/pages/commands/create-command.tsx +++ /dev/null @@ -1,56 +0,0 @@ -'use client' - -import { useState } from 'react' - -import { useParams, useRouter, useSelectedLayoutSegment } from 'next/navigation' - -import { PlusIcon } from 'lucide-react' - -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@/components/ui/dialog' - -import { CreateCommandForm } from './create-command-form' - -interface Props { - platform: Platform -} - -export function CreateCommand({ platform }: Props) { - const [open, setOpen] = useState(false) - - const segment = useSelectedLayoutSegment() - const params = useParams<{ id: string }>() - const router = useRouter() - - return ( - - - - - - - Create Command - - { - setOpen(false) - if (segment !== 'custom') { - router.push(`/dashboard/${platform}/${params.id}/commands`) - } - }} - /> - - - ) -} diff --git a/apps/web/src/components/pages/commands/update-command-form.tsx b/apps/web/src/components/pages/commands/update-command-form.tsx deleted file mode 100644 index c5722e70..00000000 --- a/apps/web/src/components/pages/commands/update-command-form.tsx +++ /dev/null @@ -1,96 +0,0 @@ -'use client' - -import { useFormStatus } from 'react-dom' - -import { toast } from 'sonner' - -import { Button } from '@/components/ui/button' -import { LoaderIcon } from '@/components/ui/icons' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { Link } from '@/components/ui/link' -import { Switch } from '@/components/ui/switch' - -import { updateEntityCommand } from '@/data-layer/actions/command' - -interface Props { - command: EntityCommand - afterSubmission: () => void -} - -export function UpdateCommandForm({ command, afterSubmission }: Props) { - return ( -
{ - // set platform fields - formData.append('id', String(command.id)) - formData.append('platform', command.platform) - formData.append('platformEntityId', command.platform_entity_id) - - const dispatch = updateEntityCommand(formData) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - afterSubmission() - return message - }, - error: ({ message }) => { - return message - }, - }) - }} - > -
-
- - -
-
- - -

- See our{' '} - - docs - {' '} - for more variables. -

-
-
- - -
-
- - - ) -} - -function SaveButton() { - const { pending } = useFormStatus() - return ( -
- -
- ) -} diff --git a/apps/web/src/components/pages/commands/update-command-status.tsx b/apps/web/src/components/pages/commands/update-command-status.tsx deleted file mode 100644 index 4bcdc597..00000000 --- a/apps/web/src/components/pages/commands/update-command-status.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client' - -import { useTransition } from 'react' - -import { toast } from 'sonner' - -import { Switch } from '@/components/ui/switch' - -import { updateEntityCommandStatus } from '@/data-layer/actions/command' - -interface Props { - command: EntityCommand -} - -export function UpdateCommandStatus({ command }: Props) { - const [pending, startTransition] = useTransition() - - return ( -
- { - startTransition(() => { - const dispatch = updateEntityCommandStatus( - command.id, - command.platform, - command.platform_entity_id, - checked, - ) - - toast.promise(dispatch, { - loading: 'Loading...', - success: ({ success, message }) => { - if (!success) { - throw new Error(message) - } - return message - }, - error: ({ message }) => { - return message - }, - }) - }) - }} - /> -
- ) -} diff --git a/apps/web/src/components/pages/commands/update-command.tsx b/apps/web/src/components/pages/commands/update-command.tsx deleted file mode 100644 index 1297f926..00000000 --- a/apps/web/src/components/pages/commands/update-command.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client' - -import { useState } from 'react' - -import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '@/components/ui/dialog' - -import { UpdateCommandForm } from './update-command-form' - -interface Props { - command: EntityCommand -} - -export function UpdateCommand({ command }: Props) { - const [open, setOpen] = useState(false) - - return ( - - - - - - - Update Command - - setOpen(false)} - /> - - - ) -} diff --git a/apps/web/src/components/pages/landing/features.tsx b/apps/web/src/components/pages/landing/features.tsx deleted file mode 100644 index dec437f0..00000000 --- a/apps/web/src/components/pages/landing/features.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { CheckCircleIcon, XIcon } from 'lucide-react' - -export function Features() { - return ( -
-
-
    -
  • - -

    - Download required -

    -
  • -
  • - -

    Free

    -
  • -
  • - -

    Open-source

    -
  • -
-
-
- ) -} diff --git a/apps/web/src/components/pages/landing/footer.tsx b/apps/web/src/components/pages/landing/footer.tsx deleted file mode 100644 index 9640e42d..00000000 --- a/apps/web/src/components/pages/landing/footer.tsx +++ /dev/null @@ -1,148 +0,0 @@ -import Image from 'next/image' - -import { Link } from '@/components/ui/link' - -const productLinks = [ - { - label: 'Dashboard', - href: '/dashboard', - }, - { - label: 'Documentation', - href: '/docs', - options: { - target: '_blank', - rel: 'noreferrer', - }, - }, -] - -const legalLinks = [ - { - label: 'Terms of Service', - href: '/terms-of-service', - }, - { - label: 'Privacy Policy', - href: '/privacy-policy', - }, - { - label: 'Cookie Policy', - href: '/cookie-policy', - }, - { - label: 'EULA', - href: '/eula', - }, -] - -const socialLinks = [ - { - label: 'Twitter', - href: 'https://x.com/senchabot', - }, - { - label: 'Discord', - href: '/discord', - }, - { - label: 'GitHub', - href: '/github', - }, - { - label: 'LinkedIn', - href: 'https://www.linkedin.com/company/senchabot/', - }, -] - -export function Footer() { - return ( -
-
-
-
-
- Senchabot -
- Senchabot -
-

- Manage your Discord and Twitch community with an open-source - multi-platform bot. -

-
-
-
- - Product - -
    - {productLinks.map((item) => ( -
  • - - {item.label} - -
  • - ))} -
-
-
- - Legal - -
    - {legalLinks.map((item) => ( -
  • - - {item.label} - -
  • - ))} -
-
-
- - Social - -
    - {socialLinks.map((item) => ( -
  • - - {item.label} - -
  • - ))} -
-
-
-
-
-
-

© {new Date().getFullYear()} Senchabot. All Rights Reserved.

-

- Made with from the - community. -

-
-
-
- ) -} diff --git a/apps/web/src/components/pages/landing/header.tsx b/apps/web/src/components/pages/landing/header.tsx deleted file mode 100644 index e9b8342b..00000000 --- a/apps/web/src/components/pages/landing/header.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import Image from 'next/image' -import NextLink from 'next/link' - -import { ThemeToggle } from '@/components/theme-toggle' -import { Button } from '@/components/ui/button' -import { Link } from '@/components/ui/link' - -const navLinks = [ - { - label: 'Documentation', - path: '/docs', - options: { - target: '_blank', - rel: 'noreferrer', - }, - }, -] - -export function Header() { - return ( -
- -
- ) -} diff --git a/apps/web/src/components/pages/landing/hero.tsx b/apps/web/src/components/pages/landing/hero.tsx deleted file mode 100644 index 4dedb0e1..00000000 --- a/apps/web/src/components/pages/landing/hero.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import NextLink from 'next/link' - -import { Button } from '@/components/ui/button' -import { GitHubIcon } from '@/components/ui/icons' -import { Link } from '@/components/ui/link' - -import { ThemedImage } from './themed-image' - -export function Hero() { - return ( -
-
-
-

- Manage Your Community from One Place -

-

- Multi-platform bot designed for seamless integration with Twitch and - Discord. -

-
- -

- We're open source on - - - GitHub - -

-
-
-
- -
-
-
- ) -} diff --git a/apps/web/src/components/pages/settings/joinable-entities.tsx b/apps/web/src/components/pages/settings/joinable-entities.tsx deleted file mode 100644 index 28960654..00000000 --- a/apps/web/src/components/pages/settings/joinable-entities.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { getUserEntities } from '@/data-layer/queries/user' - -import { Entities } from './entities' - -export async function JoinableEntities() { - const entities = await getUserEntities('not_joined') - return -} diff --git a/apps/web/src/components/pages/settings/joined-entities.tsx b/apps/web/src/components/pages/settings/joined-entities.tsx deleted file mode 100644 index d3001852..00000000 --- a/apps/web/src/components/pages/settings/joined-entities.tsx +++ /dev/null @@ -1,8 +0,0 @@ -import { getUserEntities } from '@/data-layer/queries/user' - -import { Entities } from './entities' - -export async function JoinedEntities() { - const entities = await getUserEntities('joined') - return -} diff --git a/apps/web/src/components/pages/settings/link-account.tsx b/apps/web/src/components/pages/settings/link-account.tsx deleted file mode 100644 index 460d0e63..00000000 --- a/apps/web/src/components/pages/settings/link-account.tsx +++ /dev/null @@ -1,38 +0,0 @@ -'use client' - -import { useState } from 'react' - -import { signIn } from 'next-auth/react' - -import { Button } from '@/components/ui/button' -import { LoaderIcon } from '@/components/ui/icons' - -interface Props { - platform: Platform -} - -export function LinkAccount({ platform }: Props) { - const [pending, setPending] = useState(false) - - return ( - - ) -} diff --git a/apps/web/src/components/pages/signin/signin-button.tsx b/apps/web/src/components/pages/signin/signin-button.tsx deleted file mode 100644 index 131ba0b0..00000000 --- a/apps/web/src/components/pages/signin/signin-button.tsx +++ /dev/null @@ -1,53 +0,0 @@ -'use client' - -import { useState } from 'react' - -import { signIn } from 'next-auth/react' - -import { Button } from '@/components/ui/button' -import { DiscordIcon, LoaderIcon, TwitchIcon } from '@/components/ui/icons' - -interface Props { - platform: Platform - label: string - callbackUrl?: string - isDisabled?: boolean -} - -export function SignInButton({ - platform, - label, - callbackUrl, - isDisabled = false, -}: Props) { - const [pending, setPending] = useState(false) - - return ( - - ) -} diff --git a/apps/web/src/components/pages/signin/signin-error.tsx b/apps/web/src/components/pages/signin/signin-error.tsx deleted file mode 100644 index 2b303916..00000000 --- a/apps/web/src/components/pages/signin/signin-error.tsx +++ /dev/null @@ -1,39 +0,0 @@ -'use client' - -import { useMemo } from 'react' - -import { useSearchParams } from 'next/navigation' - -import { TriangleAlertIcon } from 'lucide-react' - -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' - -type Error = 'OAuthAccountNotLinked' - -export function SignInError() { - const search = useSearchParams() - const error = search.get('error') as Error - - let errorMessage = useMemo(() => { - if (error) { - switch (error) { - case 'OAuthAccountNotLinked': - return 'The account is already associated with another user.' - default: - return 'Something went wrong!' - } - } - }, [error]) - - if (!errorMessage) { - return null - } - - return ( - - - Error - {errorMessage} - - ) -} diff --git a/apps/web/src/components/pages/signin/signin-form.tsx b/apps/web/src/components/pages/signin/signin-form.tsx deleted file mode 100644 index 99797afa..00000000 --- a/apps/web/src/components/pages/signin/signin-form.tsx +++ /dev/null @@ -1,95 +0,0 @@ -'use client' - -import { useState } from 'react' - -import { Checkbox } from '@/components/ui/checkbox' -import { Label } from '@/components/ui/label' -import { Link } from '@/components/ui/link' - -import { cn } from '@/lib/utils' - -import { SignInButton } from './signin-button' - -const platforms: { - platform: Platform - label: string -}[] = [ - { - platform: 'twitch', - label: 'Continue with Twitch', - }, - { - platform: 'discord', - label: 'Continue with Discord', - }, -] - -export function SignInForm() { - const [checked, setChecked] = useState(false) - - return ( -
-
    - {platforms.map((item) => ( -
  • - -
  • - ))} -
-
- setChecked((prev) => !prev)} - /> -
- -

- You agree to our{' '} - - Privacy Policy - - ,{' '} - - Terms of Service - - ,{' '} - - Cookie Policy - {' '} - and{' '} - - EULA - - . -

-
-
-
- ) -} diff --git a/apps/web/src/components/pages/dashboard/top-loader.tsx b/apps/web/src/components/ui/top-loader.tsx similarity index 100% rename from apps/web/src/components/pages/dashboard/top-loader.tsx rename to apps/web/src/components/ui/top-loader.tsx diff --git a/apps/web/src/env.ts b/apps/web/src/config/env.ts similarity index 100% rename from apps/web/src/env.ts rename to apps/web/src/config/env.ts diff --git a/apps/web/src/data-layer/actions/command.ts b/apps/web/src/data-layer/actions/command.ts deleted file mode 100644 index 87a718cd..00000000 --- a/apps/web/src/data-layer/actions/command.ts +++ /dev/null @@ -1,172 +0,0 @@ -'use server' - -import { revalidateTag } from 'next/cache' - -import { ApiError, fetcher } from '../fetcher' -import { - createCommandSchema, - updateCommandSchema, -} from '../validations/command' - -/** - * - * @param formData - * @returns - */ -export async function createEntityCommand( - formData: FormData, -): Promise<{ success: boolean; message: string }> { - try { - const parsed = createCommandSchema.safeParse(Object.fromEntries(formData)) - - if (!parsed.success) { - return { - success: false, - message: 'Invalid submission!', - } - } - - const { platform, platformEntityId, ...input } = parsed.data - - const params = new URLSearchParams({ platform, platformEntityId }) - await fetcher('/me/commands?' + params, { - method: 'POST', - body: JSON.stringify(input), - }) - - revalidateTag(`getEntityCommands-${platformEntityId}-custom`) - - return { - success: true, - message: 'Successfully!', - } - } catch (error) { - console.log('createEntityCommand =>', error) - if (error instanceof ApiError) { - if (error.status === 409) { - return { - success: false, - message: 'This command already exists.', - } - } else { - return { - success: false, - message: 'Something went wrong!', - } - } - } else { - return { - success: false, - message: 'Something went wrong!', - } - } - } -} - -/** - * - * @param formData - * @returns - */ -export async function updateEntityCommand( - formData: FormData, -): Promise<{ success: boolean; message: string }> { - try { - const parsed = updateCommandSchema.safeParse(Object.fromEntries(formData)) - - if (!parsed.success) { - return { - success: false, - message: 'Invalid submission!', - } - } - - const { id, platform, platformEntityId, ...input } = parsed.data - - const params = new URLSearchParams({ platform, platformEntityId }) - await fetcher(`/me/commands/${id}?` + params, { - method: 'PATCH', - body: JSON.stringify(input), - }) - - revalidateTag(`getEntityCommands-${platformEntityId}-custom`) - - return { - success: true, - message: 'Successfully!', - } - } catch (error) { - console.log('updateEntityCommand =>', error) - return { - success: false, - message: 'Something went wrong!', - } - } -} - -/** - * - * @param id - * @param platform - * @param platformEntityId - * @param status - * @returns - */ -export async function updateEntityCommandStatus( - id: number, - platform: Platform, - platformEntityId: string, - status: boolean, -): Promise<{ success: boolean; message: string }> { - try { - const params = new URLSearchParams({ platform, platformEntityId }) - await fetcher(`/me/commands/${id}?` + params, { - method: 'PATCH', - body: JSON.stringify({ status }), - }) - - return { - success: true, - message: 'Successfully!', - } - } catch (error) { - console.log('updateEntityCommandStatus =>', error) - return { - success: false, - message: 'Something went wrong!', - } - } -} - -/** - * - * @param id - * @param platform - * @param platformEntityId - * @returns - */ -export async function deleteEntityCommand( - id: number, - platform: Platform, - platformEntityId: string, -): Promise<{ success: boolean; message: string }> { - try { - const params = new URLSearchParams({ platform, platformEntityId }) - await fetcher(`/me/commands/${id}?` + params, { - method: 'DELETE', - }) - - revalidateTag(`getEntityCommands-${platformEntityId}-custom`) - - return { - success: true, - message: 'Successfully!', - } - } catch (error) { - console.log('deleteEntityCommand =>', error) - return { - success: false, - message: 'Something went wrong!', - } - } -} diff --git a/apps/web/src/data-layer/actions/entity.ts b/apps/web/src/data-layer/actions/entity.ts deleted file mode 100644 index a3f1583b..00000000 --- a/apps/web/src/data-layer/actions/entity.ts +++ /dev/null @@ -1,60 +0,0 @@ -'use server' - -import { revalidateTag } from 'next/cache' - -import { fetcher } from '../fetcher' - -/** - * - * @param provider - * @param providerAccountId - * @param userId - * @returns - */ -export async function linkEntity( - provider: string, - providerAccountId: string, - userId: string, -) { - return fetcher('/platforms/link', { - method: 'POST', - body: JSON.stringify({ - provider: provider, - provider_account_id: providerAccountId, - user_id: userId, - }), - }) -} - -/** - * - * @param action - * @param platform - * @param platformEntityId - * @returns - */ -export async function executeEntityAction( - action: 'join' | 'depart', - platform: Platform, - platformEntityId: string, -): Promise<{ success: boolean; message: string }> { - try { - const params = new URLSearchParams({ platform, platformEntityId }) - await fetcher(`/me/platforms/actions/${action}?` + params, { - method: 'POST', - }) - - revalidateTag('getUserEntities') - - return { - success: true, - message: 'Successfully!', - } - } catch (error) { - console.log('executeEntityAction =>', error) - return { - success: false, - message: 'Something went wrong!', - } - } -} diff --git a/apps/web/src/data-layer/queries/entity.ts b/apps/web/src/data-layer/queries/entity.ts deleted file mode 100644 index 8dec5ef8..00000000 --- a/apps/web/src/data-layer/queries/entity.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { fetcher } from '../fetcher' - -/** - * - * @param platform - * @param platformEntityId - * @returns - */ -export async function getEntityLogs( - platform: Platform, - platformEntityId: string, -): Promise { - const params = new URLSearchParams({ - noCache: 'true', - platform, - platformEntityId, - }) - - return fetcher('/me/platforms/logs?' + params, { - next: { - tags: [`getEntityLogs-${platformEntityId}`], - }, - }) -} - -/** - * - * @param platform - * @param platformEntityId - * @param type - * @returns - */ -export async function getEntityCommands( - platform: Platform, - platformEntityId: string, - type: CommandType, -): Promise { - const params = new URLSearchParams({ - noCache: 'true', - platform, - platformEntityId, - type, - }) - return fetcher('/me/commands?' + params, { - next: { - tags: [`getEntityCommands-${platformEntityId}-${type}`], - }, - }) -} diff --git a/apps/web/src/data-layer/validations/index.ts b/apps/web/src/data-layer/validations/index.ts deleted file mode 100644 index 20999ed6..00000000 --- a/apps/web/src/data-layer/validations/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { z } from 'zod' - -export const platform = z.enum(['twitch', 'discord']) diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index a7eed266..35480084 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -4,7 +4,7 @@ import Twitch from 'next-auth/providers/twitch' import { PrismaAdapter } from '@auth/prisma-adapter' -import { linkEntity } from '@/data-layer/actions/entity' +import { linkEntity } from '@/services/entities/actions' import { prisma } from './db' @@ -13,7 +13,11 @@ export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [Twitch, Discord], events: { linkAccount: ({ account, user }) => { - linkEntity(account.provider, account.providerAccountId, user.id!) + linkEntity({ + provider: account.provider as 'twitch' | 'discord', + providerAccountId: account.providerAccountId, + userId: user.id!, + }) }, }, pages: { diff --git a/apps/web/src/data-layer/fetcher.ts b/apps/web/src/lib/fetcher.ts similarity index 97% rename from apps/web/src/data-layer/fetcher.ts rename to apps/web/src/lib/fetcher.ts index 8d0f24a0..5ab9f295 100644 --- a/apps/web/src/data-layer/fetcher.ts +++ b/apps/web/src/lib/fetcher.ts @@ -1,7 +1,7 @@ import { cookies } from 'next/headers' import { notFound } from 'next/navigation' -import { env } from '@/env' +import { env } from '@/config/env' const BASE_URL = env.API_URL diff --git a/apps/web/src/services/auth/actions.ts b/apps/web/src/services/auth/actions.ts new file mode 100644 index 00000000..5cd17d23 --- /dev/null +++ b/apps/web/src/services/auth/actions.ts @@ -0,0 +1,31 @@ +'use server' + +import { createServerAction } from 'zsa' + +import { signIn as _signIn, signOut as _signOut } from '@/lib/auth' + +import { signInWithProviderSchema } from './schema' + +/** + * + */ +export const signInWithProvider = createServerAction() + .input(signInWithProviderSchema) + .handler(async ({ input }) => { + if (input.provider === 'twitch') { + await _signIn(input.provider, { reditectTo: input.redirectTo }) + } else if (input.provider === 'discord') { + await _signIn( + input.provider, + { reditectTo: input.redirectTo }, + { scope: ['identify', 'email', 'guilds'].join(' ') }, + ) + } + }) + +/** + * + */ +export const signOut = createServerAction().handler(async () => { + await _signOut({ redirectTo: '/signin' }) +}) diff --git a/apps/web/src/services/auth/schema.ts b/apps/web/src/services/auth/schema.ts new file mode 100644 index 00000000..197ac46e --- /dev/null +++ b/apps/web/src/services/auth/schema.ts @@ -0,0 +1,8 @@ +import { z } from 'zod' + +import { platformEnum } from '../shared/schema' + +export const signInWithProviderSchema = z.object({ + provider: platformEnum, + redirectTo: z.string().min(1), +}) diff --git a/apps/web/src/services/commands/actions.ts b/apps/web/src/services/commands/actions.ts new file mode 100644 index 00000000..0ad4ca24 --- /dev/null +++ b/apps/web/src/services/commands/actions.ts @@ -0,0 +1,106 @@ +'use server' + +import { revalidateTag } from 'next/cache' + +import { ZSAError, createServerAction } from 'zsa' + +import { ApiError, fetcher } from '@/lib/fetcher' + +import { + createCommandSchema, + deleteCommandSchema, + setCommandStatusSchema, + updateCommandSchema, +} from './schema' + +/** + * + */ +export const createCommand = createServerAction() + .input(createCommandSchema, { type: 'formData' }) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platform', input.platform) + params.append('platformEntityId', input.platformEntityId) + + await fetcher('/me/commands?' + params, { + method: 'POST', + body: JSON.stringify(input), + }) + + revalidateTag(`getEntityCommands-${input.platformEntityId}-custom`) + } catch (error) { + console.error('updateEntityCommand =>', error) + if (error instanceof ApiError) { + if (error.status === 409) { + throw new ZSAError('CONFLICT', 'This command already exists!') + } + } + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) + +/** + * + */ +export const updateCommand = createServerAction() + .input(updateCommandSchema, { type: 'formData' }) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platform', input.platform) + params.append('platformEntityId', input.platformEntityId) + + await fetcher(`/me/commands/${input.id}?` + params, { + method: 'PATCH', + body: JSON.stringify(input), + }) + } catch (error) { + console.error('updateEntityCommand =>', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) + +/** + * + */ +export const setCommandStatus = createServerAction() + .input(setCommandStatusSchema) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platform', input.platform) + params.append('platformEntityId', input.platformEntityId) + + await fetcher(`/me/commands/${input.id}?` + params, { + method: 'PATCH', + body: JSON.stringify(input), + }) + } catch (error) { + console.error('setCommandStatus =>', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) + +/** + * + */ +export const deleteCommand = createServerAction() + .input(deleteCommandSchema) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platform', input.platform) + params.append('platformEntityId', input.platformEntityId) + + await fetcher(`/me/commands/${input.id}?` + params, { + method: 'DELETE', + }) + + revalidateTag(`getEntityCommands-${input.platformEntityId}-custom`) + } catch (error) { + console.error('deleteCommand =>', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) diff --git a/apps/web/src/services/commands/queries.ts b/apps/web/src/services/commands/queries.ts new file mode 100644 index 00000000..02799a75 --- /dev/null +++ b/apps/web/src/services/commands/queries.ts @@ -0,0 +1,29 @@ +import { fetcher } from '@/lib/fetcher' + +import type { Platform } from '../shared/type' +import { EntityCommand } from './type' + +/** + * + * @param platform + * @param platformEntityId + * @param type + * @returns + */ +export async function getCommands( + platform: Platform, + platformEntityId: string, + type: 'custom' | 'global', +): Promise { + const params = new URLSearchParams() + params.append('noCache', 'true') + params.append('platform', platform) + params.append('platformEntityId', platformEntityId) + params.append('type', type) + + return fetcher('/me/commands?' + params, { + next: { + tags: [`getEntityCommands-${platformEntityId}-${type}`], + }, + }) +} diff --git a/apps/web/src/data-layer/validations/command.ts b/apps/web/src/services/commands/schema.ts similarity index 50% rename from apps/web/src/data-layer/validations/command.ts rename to apps/web/src/services/commands/schema.ts index 8f935b9f..a61f89b0 100644 --- a/apps/web/src/data-layer/validations/command.ts +++ b/apps/web/src/services/commands/schema.ts @@ -1,9 +1,9 @@ import { z } from 'zod' -import { platform } from '.' +import { platformEnum } from '../shared/schema' export const createCommandSchema = z.object({ - platform: platform, + platform: platformEnum, platformEntityId: z.string().min(1), command_name: z .string() @@ -14,9 +14,22 @@ export const createCommandSchema = z.object({ }) export const updateCommandSchema = z.object({ - id: z.string(), - platform: platform, + id: z.number(), + platform: platformEnum, platformEntityId: z.string().min(1), command_content: z.string().min(1), status: z.coerce.boolean(), }) + +export const setCommandStatusSchema = z.object({ + id: z.number(), + platform: platformEnum, + platformEntityId: z.string().min(1), + status: z.coerce.boolean(), +}) + +export const deleteCommandSchema = z.object({ + id: z.number(), + platform: platformEnum, + platformEntityId: z.string().min(1), +}) diff --git a/apps/web/src/services/commands/type.ts b/apps/web/src/services/commands/type.ts new file mode 100644 index 00000000..d9f51a16 --- /dev/null +++ b/apps/web/src/services/commands/type.ts @@ -0,0 +1,14 @@ +import type { Platform } from '../shared/type' + +export type EntityCommand = { + id: number + name: string + content: string + status: boolean + platform: Platform + platform_entity_id: string + type: number + created_by: string + updated_by: string + created_at: string +} diff --git a/apps/web/src/services/entities/actions.ts b/apps/web/src/services/entities/actions.ts new file mode 100644 index 00000000..af56b17c --- /dev/null +++ b/apps/web/src/services/entities/actions.ts @@ -0,0 +1,47 @@ +'use server' + +import { revalidateTag } from 'next/cache' + +import { ZSAError, createServerAction } from 'zsa' + +import { fetcher } from '@/lib/fetcher' + +import { executeEntityActionSchema, linkEntitySchema } from './schema' + +/** + * + */ +export const linkEntity = createServerAction() + .input(linkEntitySchema) + .handler(async ({ input }) => { + await fetcher('/platforms/link', { + method: 'POST', + body: JSON.stringify({ + provider: input.provider, + provider_account_id: input.providerAccountId, + user_id: input.userId, + }), + }) + }) + +/** + * + */ +export const executeEntityAction = createServerAction() + .input(executeEntityActionSchema) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platform', input.platform) + params.append('platformEntityId', input.platformEntityId) + + await fetcher(`/me/platforms/actions/${input.action}?` + params, { + method: 'POST', + }) + + revalidateTag('getUserEntities') + } catch (error) { + console.error('executeEntityAction => ', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) diff --git a/apps/web/src/services/entities/queries.ts b/apps/web/src/services/entities/queries.ts new file mode 100644 index 00000000..464a9649 --- /dev/null +++ b/apps/web/src/services/entities/queries.ts @@ -0,0 +1,26 @@ +import { fetcher } from '@/lib/fetcher' + +import type { Platform } from '../shared/type' +import { EntityLog } from './type' + +/** + * + * @param platform + * @param platformEntityId + * @returns + */ +export async function getEntityLogs( + platform: Platform, + platformEntityId: string, +): Promise { + const params = new URLSearchParams() + params.append('noCache', 'true') + params.append('platform', platform) + params.append('platformEntityId', platformEntityId) + + return fetcher('/me/platforms/logs?' + params, { + next: { + tags: [`getEntityLogs-${platformEntityId}`], + }, + }) +} diff --git a/apps/web/src/services/entities/schema.ts b/apps/web/src/services/entities/schema.ts new file mode 100644 index 00000000..86520481 --- /dev/null +++ b/apps/web/src/services/entities/schema.ts @@ -0,0 +1,15 @@ +import { z } from 'zod' + +import { platformEnum } from '../shared/schema' + +export const linkEntitySchema = z.object({ + provider: platformEnum, + providerAccountId: z.string().min(1), + userId: z.string().min(1), +}) + +export const executeEntityActionSchema = z.object({ + action: z.enum(['join', 'depart']), + platform: platformEnum, + platformEntityId: z.string().min(1), +}) diff --git a/apps/web/src/services/entities/type.ts b/apps/web/src/services/entities/type.ts new file mode 100644 index 00000000..d5962e24 --- /dev/null +++ b/apps/web/src/services/entities/type.ts @@ -0,0 +1,11 @@ +import type { Platform } from '../shared/type' + +export type EntityLog = { + id: string + author: string + author_id: string + activity: string + activity_date: Date + platform: Platform + platform_entity_id: string +} diff --git a/apps/web/src/services/shared/schema.ts b/apps/web/src/services/shared/schema.ts new file mode 100644 index 00000000..304696cf --- /dev/null +++ b/apps/web/src/services/shared/schema.ts @@ -0,0 +1,3 @@ +import { z } from 'zod' + +export const platformEnum = z.enum(['twitch', 'discord']) diff --git a/apps/web/src/services/shared/type.ts b/apps/web/src/services/shared/type.ts new file mode 100644 index 00000000..69a51c8e --- /dev/null +++ b/apps/web/src/services/shared/type.ts @@ -0,0 +1,5 @@ +import { z } from 'zod' + +import { platformEnum } from './schema' + +export type Platform = z.infer diff --git a/apps/web/src/data-layer/queries/user.ts b/apps/web/src/services/users/queries.ts similarity index 62% rename from apps/web/src/data-layer/queries/user.ts rename to apps/web/src/services/users/queries.ts index 6793e0c9..5326c100 100644 --- a/apps/web/src/data-layer/queries/user.ts +++ b/apps/web/src/services/users/queries.ts @@ -1,11 +1,16 @@ -import { fetcher } from '../fetcher' +import { fetcher } from '@/lib/fetcher' + +import type { UserAccount, UserEntity } from './type' /** * * @returns */ export async function getUserAccounts(): Promise { - return fetcher('/me/accounts?noCache=true', { + const params = new URLSearchParams() + params.append('noCache', 'true') + + return fetcher('/me/accounts?' + params, { next: { tags: ['getUserAccounts'], }, @@ -20,7 +25,8 @@ export async function getUserAccounts(): Promise { export async function getUserEntities( type?: 'joined' | 'not_joined', ): Promise { - const params = new URLSearchParams({ noCache: 'true' }) + const params = new URLSearchParams() + params.append('noCache', 'true') if (type) { params.append('joined', type === 'joined' ? 'true' : 'false') diff --git a/apps/web/src/services/users/type.ts b/apps/web/src/services/users/type.ts new file mode 100644 index 00000000..5569277e --- /dev/null +++ b/apps/web/src/services/users/type.ts @@ -0,0 +1,19 @@ +import type { Platform } from '../shared/type' + +export type UserAccount = { + user_id: string + account_username: string + provider: Platform + provider_account_id: string + created_at: Date + updated_at: Date +} + +export type UserEntity = { + entity_name: string + entity_icon: string + entity_owner_id: string + entity_bot_joined: boolean + platform: Platform + platform_entity_id: string +} diff --git a/apps/web/src/types/index.ts b/apps/web/src/types/index.ts deleted file mode 100644 index feb4f290..00000000 --- a/apps/web/src/types/index.ts +++ /dev/null @@ -1,43 +0,0 @@ -type Platform = 'twitch' | 'discord' -type CommandType = 'custom' | 'global' - -type UserAccount = { - user_id: string - account_username: string - provider: Platform - provider_account_id: string - created_at: Date - updated_at: Date -} - -type UserEntity = { - entity_name: string - entity_icon: string - entity_owner_id: string - entity_bot_joined: boolean - platform: Platform - platform_entity_id: string -} - -type EntityLog = { - id: string - author: string - author_id: string - activity: string - activity_date: Date - platform: Platform - platform_entity_id: string -} - -type EntityCommand = { - id: number - name: string - content: string - status: boolean - platform: Platform - platform_entity_id: string - type: number - created_by: string - updated_by: string - created_at: string -} From da93759d753532fb198b2e2d102e8f3c2b883b44 Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 14 Jul 2024 11:05:49 +0300 Subject: [PATCH 03/16] chore(apps/web): upgrade `next@14.2.5` --- apps/web/package.json | 2 +- apps/web/pnpm-lock.yaml | 114 ++++++++++++++++++++-------------------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index 6488ba1f..5a144644 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -29,7 +29,7 @@ "geist": "^1.3.0", "jiti": "^1.21.0", "lucide-react": "^0.378.0", - "next": "14.2.3", + "next": "14.2.5", "next-auth": "5.0.0-beta.17", "next-themes": "^0.3.0", "nextjs-toploader": "^1.6.12", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index a852fbb7..6b020743 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.0.0(@prisma/client@5.13.0(prisma@5.13.0)) '@next/third-parties': specifier: ^14.2.3 - version: 14.2.3(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 14.2.3(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@prisma/client': specifier: ^5.13.0 version: 5.13.0(prisma@5.13.0) @@ -61,7 +61,7 @@ importers: version: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) geist: specifier: ^1.3.0 - version: 1.3.0(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.3.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) jiti: specifier: ^1.21.0 version: 1.21.0 @@ -69,17 +69,17 @@ importers: specifier: ^0.378.0 version: 0.378.0(react@18.3.1) next: - specifier: 14.2.3 - version: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 14.2.5 + version: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-auth: specifier: 5.0.0-beta.17 - version: 5.0.0-beta.17(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 5.0.0-beta.17(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) next-themes: specifier: ^0.3.0 version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nextjs-toploader: specifier: ^1.6.12 - version: 1.6.12(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 1.6.12(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: specifier: ^18.3.1 version: 18.3.1 @@ -304,62 +304,62 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@next/env@14.2.3': - resolution: {integrity: sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA==} + '@next/env@14.2.5': + resolution: {integrity: sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==} '@next/eslint-plugin-next@14.2.3': resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} - '@next/swc-darwin-arm64@14.2.3': - resolution: {integrity: sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==} + '@next/swc-darwin-arm64@14.2.5': + resolution: {integrity: sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.3': - resolution: {integrity: sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==} + '@next/swc-darwin-x64@14.2.5': + resolution: {integrity: sha512-vXHOPCwfDe9qLDuq7U1OYM2wUY+KQ4Ex6ozwsKxp26BlJ6XXbHleOUldenM67JRyBfVjv371oneEvYd3H2gNSA==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.3': - resolution: {integrity: sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==} + '@next/swc-linux-arm64-gnu@14.2.5': + resolution: {integrity: sha512-vlhB8wI+lj8q1ExFW8lbWutA4M2ZazQNvMWuEDqZcuJJc78iUnLdPPunBPX8rC4IgT6lIx/adB+Cwrl99MzNaA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.3': - resolution: {integrity: sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==} + '@next/swc-linux-arm64-musl@14.2.5': + resolution: {integrity: sha512-NpDB9NUR2t0hXzJJwQSGu1IAOYybsfeB+LxpGsXrRIb7QOrYmidJz3shzY8cM6+rO4Aojuef0N/PEaX18pi9OA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.3': - resolution: {integrity: sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==} + '@next/swc-linux-x64-gnu@14.2.5': + resolution: {integrity: sha512-8XFikMSxWleYNryWIjiCX+gU201YS+erTUidKdyOVYi5qUQo/gRxv/3N1oZFCgqpesN6FPeqGM72Zve+nReVXQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.3': - resolution: {integrity: sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==} + '@next/swc-linux-x64-musl@14.2.5': + resolution: {integrity: sha512-6QLwi7RaYiQDcRDSU/os40r5o06b5ue7Jsk5JgdRBGGp8l37RZEh9JsLSM8QF0YDsgcosSeHjglgqi25+m04IQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.3': - resolution: {integrity: sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==} + '@next/swc-win32-arm64-msvc@14.2.5': + resolution: {integrity: sha512-1GpG2VhbspO+aYoMOQPQiqc/tG3LzmsdBH0LhnDS3JrtDx2QmzXe0B6mSZZiN3Bq7IOMXxv1nlsjzoS1+9mzZw==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.3': - resolution: {integrity: sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==} + '@next/swc-win32-ia32-msvc@14.2.5': + resolution: {integrity: sha512-Igh9ZlxwvCDsu6438FXlQTHlRno4gFpJzqPjSIBZooD22tKeI4fE/YMRoHVJHmrQ2P5YL1DoZ0qaOKkbeFWeMg==} engines: {node: '>= 10'} cpu: [ia32] os: [win32] - '@next/swc-win32-x64-msvc@14.2.3': - resolution: {integrity: sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==} + '@next/swc-win32-x64-msvc@14.2.5': + resolution: {integrity: sha512-tEQ7oinq1/CjSG9uSTerca3v4AZ+dFa+4Yu6ihaG8Ud8ddqLQgFGcnwYls13H5X5CPDPZJdYxyeMui6muOLd4g==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1788,8 +1788,8 @@ packages: react: ^16.8 || ^17 || ^18 react-dom: ^16.8 || ^17 || ^18 - next@14.2.3: - resolution: {integrity: sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==} + next@14.2.5: + resolution: {integrity: sha512-0f8aRfBVL+mpzfBjYfQuLWh2WyAwtJXCRfkPF4UJ5qd2YwrHczsrSzXU4tRMV0OAxR8ZJZWPFn6uhSC56UTsLA==} engines: {node: '>=18.17.0'} hasBin: true peerDependencies: @@ -2631,42 +2631,42 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@next/env@14.2.3': {} + '@next/env@14.2.5': {} '@next/eslint-plugin-next@14.2.3': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.3': + '@next/swc-darwin-arm64@14.2.5': optional: true - '@next/swc-darwin-x64@14.2.3': + '@next/swc-darwin-x64@14.2.5': optional: true - '@next/swc-linux-arm64-gnu@14.2.3': + '@next/swc-linux-arm64-gnu@14.2.5': optional: true - '@next/swc-linux-arm64-musl@14.2.3': + '@next/swc-linux-arm64-musl@14.2.5': optional: true - '@next/swc-linux-x64-gnu@14.2.3': + '@next/swc-linux-x64-gnu@14.2.5': optional: true - '@next/swc-linux-x64-musl@14.2.3': + '@next/swc-linux-x64-musl@14.2.5': optional: true - '@next/swc-win32-arm64-msvc@14.2.3': + '@next/swc-win32-arm64-msvc@14.2.5': optional: true - '@next/swc-win32-ia32-msvc@14.2.3': + '@next/swc-win32-ia32-msvc@14.2.5': optional: true - '@next/swc-win32-x64-msvc@14.2.3': + '@next/swc-win32-x64-msvc@14.2.5': optional: true - '@next/third-parties@14.2.3(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@next/third-parties@14.2.3(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: - next: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 third-party-capital: 1.0.20 @@ -3913,9 +3913,9 @@ snapshots: functions-have-names@1.2.3: {} - geist@1.3.0(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + geist@1.3.0(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: - next: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) get-intrinsic@1.2.4: dependencies: @@ -4273,10 +4273,10 @@ snapshots: natural-compare@1.4.0: {} - next-auth@5.0.0-beta.17(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): + next-auth@5.0.0-beta.17(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1): dependencies: '@auth/core': 0.30.0 - next: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 next-themes@0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): @@ -4284,9 +4284,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - '@next/env': 14.2.3 + '@next/env': 14.2.5 '@swc/helpers': 0.5.5 busboy: 1.6.0 caniuse-lite: 1.0.30001618 @@ -4296,22 +4296,22 @@ snapshots: react-dom: 18.3.1(react@18.3.1) styled-jsx: 5.1.1(react@18.3.1) optionalDependencies: - '@next/swc-darwin-arm64': 14.2.3 - '@next/swc-darwin-x64': 14.2.3 - '@next/swc-linux-arm64-gnu': 14.2.3 - '@next/swc-linux-arm64-musl': 14.2.3 - '@next/swc-linux-x64-gnu': 14.2.3 - '@next/swc-linux-x64-musl': 14.2.3 - '@next/swc-win32-arm64-msvc': 14.2.3 - '@next/swc-win32-ia32-msvc': 14.2.3 - '@next/swc-win32-x64-msvc': 14.2.3 + '@next/swc-darwin-arm64': 14.2.5 + '@next/swc-darwin-x64': 14.2.5 + '@next/swc-linux-arm64-gnu': 14.2.5 + '@next/swc-linux-arm64-musl': 14.2.5 + '@next/swc-linux-x64-gnu': 14.2.5 + '@next/swc-linux-x64-musl': 14.2.5 + '@next/swc-win32-arm64-msvc': 14.2.5 + '@next/swc-win32-ia32-msvc': 14.2.5 + '@next/swc-win32-x64-msvc': 14.2.5 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros - nextjs-toploader@1.6.12(next@14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + nextjs-toploader@1.6.12(next@14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - next: 14.2.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 14.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nprogress: 0.2.0 prop-types: 15.8.1 react: 18.3.1 From 760032d8c8b12acc776088b8bbedc80869182efc Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 14 Jul 2024 11:12:56 +0300 Subject: [PATCH 04/16] feat(apps/web): add `platform` label to before entity-names --- .../(protected)/dashboard/_sidebar/entities-dropdown.tsx | 6 ++++++ .../app/(protected)/dashboard/joined-entities-list.tsx | 9 +++++++-- .../dashboard/settings/@tabs/servers/entities-list.tsx | 7 ++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx index 24b799b4..f42ae072 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx @@ -60,6 +60,9 @@ export function EntitiesDropdown({ entities }: Props) { + + {selectedEntity.platform} + {selectedEntity.entity_name}
@@ -93,6 +96,9 @@ export function EntitiesDropdown({ entities }: Props) { + + {item.platform} + {item.entity_name}
diff --git a/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx index b2361c70..7a53e673 100644 --- a/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx @@ -21,7 +21,7 @@ export async function JoinedEntitiesList() {
{entities.map((item) => ( @@ -29,7 +29,12 @@ export async function JoinedEntitiesList() { {item.entity_name.charAt(0)} - {item.entity_name} + + + {item.platform} + + {item.entity_name} + ))}
diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx index 1b04b8fc..45d29d51 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx @@ -27,7 +27,12 @@ export function EntitiesList({ entities }: Props) { {item.entity_name.charAt(0)} - {item.entity_name} + + + {item.platform} + + {item.entity_name} +
{item.entity_bot_joined ? ( Date: Sun, 14 Jul 2024 11:46:23 +0300 Subject: [PATCH 05/16] refactor(apps/web): `commands-list` --- .../[platform]/[id]/commands/@tabs/commands-list.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx index b76d0692..5ac9be59 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx @@ -45,7 +45,7 @@ export async function CommandsList({ platform, id, type }: Props) { - !{item.name} + {item.name}

{item.content} @@ -70,7 +70,7 @@ export async function CommandsList({ platform, id, type }: Props) { } else if (type === 'global') { return (

- +
Name @@ -80,9 +80,9 @@ export async function CommandsList({ platform, id, type }: Props) { {commands.map((item) => ( - !{item.name} + {item.name} -

+

{item.content}

From e2513c72f08328be78ce68f057f5bf2c23d02d3e Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 14 Jul 2024 11:58:48 +0300 Subject: [PATCH 06/16] refactor(apps/web): `create-command` & `share-command` button changes --- .../[platform]/[id]/commands/create-command-dialog.tsx | 2 +- .../dashboard/[platform]/[id]/commands/layout.tsx | 8 +++++--- .../[platform]/[id]/commands/share-commands-button.tsx | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx index 1fce90e7..912a552c 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx @@ -35,7 +35,7 @@ export function CreateCommand({ platform, entityId }: Props) { return ( - diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx index 15aa1825..5eb4b6e7 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx @@ -15,10 +15,12 @@ export default function Layout({ params, tabs }: Props) { return (
-
+

Commands

- - +
+ + +

Manage your commands.

diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx index 27022298..b9707dd8 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/share-commands-button.tsx @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button' export function ShareCommands() { return ( - From 31dd026ad1e0a586c7e820a736c627e826f6532a Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 14 Jul 2024 12:12:25 +0300 Subject: [PATCH 07/16] refactor(apps/web): `entities-dropdown` popover width --- .../(protected)/dashboard/_sidebar/entities-dropdown.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx index f42ae072..172a06ae 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx @@ -72,7 +72,10 @@ export function EntitiesDropdown({ entities }: Props) { - + No server found. @@ -95,7 +98,7 @@ export function EntitiesDropdown({ entities }: Props) { {item.entity_name.charAt(0)} - + {item.platform} From 191ad0425bcbcfdf063b05c048b5e2c990c402f3 Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 21 Jul 2024 12:39:35 +0300 Subject: [PATCH 08/16] fix(apps/web): added missing scope to discord join --- .../dashboard/settings/@tabs/servers/join-entity-button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx index 00bf10ec..f8e75ed2 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx @@ -51,7 +51,7 @@ export function JoinEntity({ params.append('guild_id', platformEntityId) params.append('disable_guild_select', 'true') params.append('permission', '2199022698327') - params.append('scope', 'applications.commands') + params.append('scope', ['bot', 'applications.commands'].join(' ')) window.open(new URL(BASE_URL + params), '_blank', 'noreferrer') } From 314aee720249cb0bcf3d30433f00d5794a7b551a Mon Sep 17 00:00:00 2001 From: eckoln Date: Sat, 10 Aug 2024 12:51:06 +0300 Subject: [PATCH 09/16] feat(apps/web): add cached use-session hook --- apps/web/src/app/(auth)/signin/page.tsx | 6 +++--- .../[platform]/[id]/commands/@tabs/custom/page.tsx | 6 +++--- .../[platform]/[id]/commands/@tabs/global/page.tsx | 6 +++--- .../app/(protected)/dashboard/[platform]/[id]/page.tsx | 6 +++--- .../app/(protected)/dashboard/_sidebar/user-dropdown.tsx | 6 ++++-- apps/web/src/app/(protected)/dashboard/page.tsx | 6 +++--- .../(protected)/dashboard/settings/@tabs/privacy/page.tsx | 6 +++--- .../(protected)/dashboard/settings/@tabs/profile/page.tsx | 6 +++--- .../settings/@tabs/profile/personal-information.tsx | 5 +++-- .../(protected)/dashboard/settings/@tabs/servers/page.tsx | 6 +++--- apps/web/src/hooks/use-session.tsx | 8 ++++++++ 11 files changed, 39 insertions(+), 28 deletions(-) create mode 100644 apps/web/src/hooks/use-session.tsx diff --git a/apps/web/src/app/(auth)/signin/page.tsx b/apps/web/src/app/(auth)/signin/page.tsx index 45e3f985..f4b3dd69 100644 --- a/apps/web/src/app/(auth)/signin/page.tsx +++ b/apps/web/src/app/(auth)/signin/page.tsx @@ -7,7 +7,7 @@ import { ArrowLeft, TriangleAlertIcon } from 'lucide-react' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import { SignInForm } from './signin-form' @@ -32,11 +32,11 @@ interface Props { } export default async function Page({ searchParams }: Props) { - const session = await auth() + const session = await useSession() // show error if user received an error while linking an account if (session && !searchParams.error) { - redirect('/dashboard') + throw redirect('/dashboard') } const errorMessage = getErrorMessage(searchParams.error) diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx index f685260b..ab9f7e83 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import type { Platform } from '@/services/shared/type' @@ -19,10 +19,10 @@ interface Props { } export default async function Page({ params }: Props) { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx index ba89e4fb..ca4e93ad 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx @@ -1,7 +1,7 @@ import type { Metadata } from 'next' import { redirect } from 'next/navigation' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import type { Platform } from '@/services/shared/type' @@ -19,10 +19,10 @@ interface Props { } export default async function Page({ params }: Props) { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx index 86b1b091..a72da0e9 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx @@ -5,7 +5,7 @@ import { redirect } from 'next/navigation' import { LoaderIcon } from '@/components/ui/icons' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import type { Platform } from '@/services/shared/type' @@ -23,10 +23,10 @@ interface Props { } export default async function Page({ params }: Props) { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx index 11836f56..64cb4987 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/user-dropdown.tsx @@ -12,10 +12,12 @@ import { DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' -import { auth, signOut } from '@/lib/auth' +import { signOut } from '@/lib/auth' + +import { useSession } from '@/hooks/use-session' export async function UserDropdown() { - const session = await auth() + const session = await useSession() if (!session?.user) { return null diff --git a/apps/web/src/app/(protected)/dashboard/page.tsx b/apps/web/src/app/(protected)/dashboard/page.tsx index d6f0fd3b..645ce762 100644 --- a/apps/web/src/app/(protected)/dashboard/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/page.tsx @@ -5,7 +5,7 @@ import { redirect } from 'next/navigation' import { LoaderIcon } from '@/components/ui/icons' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import { JoinedEntitiesList } from './joined-entities-list' @@ -14,10 +14,10 @@ export const metadata: Metadata = { } export default async function Page() { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/privacy/page.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/privacy/page.tsx index 5af88696..c87d6e57 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/privacy/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/privacy/page.tsx @@ -4,17 +4,17 @@ import { redirect } from 'next/navigation' import { Button } from '@/components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' export const metadata: Metadata = { title: 'Privacy', } export default async function Page() { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx index a5e0e408..c0fcd4d7 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/page.tsx @@ -12,7 +12,7 @@ import { } from '@/components/ui/card' import { LoaderIcon } from '@/components/ui/icons' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import { LinkedAccounts } from './linked-accounts-list' import { PersonalInformation } from './personal-information' @@ -22,10 +22,10 @@ export const metadata: Metadata = { } export default async function Page() { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx index 2fa3a003..e7c06aba 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/personal-information.tsx @@ -1,11 +1,12 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' -import { auth } from '@/lib/auth' import { maskEmail } from '@/lib/utils' +import { useSession } from '@/hooks/use-session' + export async function PersonalInformation() { - const session = await auth() + const session = await useSession() if (!session?.user) { return null diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx index d4f65a14..b47d9d00 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/page.tsx @@ -12,7 +12,7 @@ import { } from '@/components/ui/card' import { LoaderIcon } from '@/components/ui/icons' -import { auth } from '@/lib/auth' +import { useSession } from '@/hooks/use-session' import { JoinableEntities } from './joinable-entities-list' import { JoinedEntities } from './joined-entities-list' @@ -22,10 +22,10 @@ export const metadata: Metadata = { } export default async function Page() { - const session = await auth() + const session = await useSession() if (!session) { - redirect('/signin') + throw redirect('/signin') } return ( diff --git a/apps/web/src/hooks/use-session.tsx b/apps/web/src/hooks/use-session.tsx new file mode 100644 index 00000000..d182da91 --- /dev/null +++ b/apps/web/src/hooks/use-session.tsx @@ -0,0 +1,8 @@ +import { cache } from 'react' + +import { auth } from '@/lib/auth' + +export const useSession = cache(async () => { + const session = await auth() + return session +}) From 241750870772b0a9ce529b81c1a965a72b478289 Mon Sep 17 00:00:00 2001 From: eckoln Date: Fri, 23 Aug 2024 12:20:06 +0300 Subject: [PATCH 10/16] refactor(apps/web)!: codebase --- apps/web/src/app/(auth)/signin/signin-button.tsx | 2 +- .../[id]/commands/@tabs/command-status-switch.tsx | 5 +++-- .../[platform]/[id]/commands/@tabs/commands-list.tsx | 5 +++-- .../[platform]/[id]/commands/@tabs/custom/page.tsx | 2 +- .../[id]/commands/@tabs/delete-command-button.tsx | 5 +++-- .../[platform]/[id]/commands/@tabs/global/page.tsx | 2 +- .../[platform]/[id]/commands/@tabs/layout.tsx | 2 +- .../[id]/commands/@tabs/update-command-dialog.tsx | 5 +++-- .../[platform]/[id]/commands/create-command-dialog.tsx | 5 +++-- .../dashboard/[platform]/[id]/commands/layout.tsx | 2 +- .../dashboard/[platform]/[id]/entity-logs-card.tsx | 5 +++-- .../app/(protected)/dashboard/[platform]/[id]/page.tsx | 2 +- .../dashboard/_sidebar/entities-dropdown-wrapper.tsx | 2 +- .../dashboard/_sidebar/entities-dropdown.tsx | 2 +- .../app/(protected)/dashboard/joined-entities-list.tsx | 2 +- .../settings/@tabs/profile/link-account-button.tsx | 2 +- .../settings/@tabs/profile/linked-accounts-list.tsx | 2 +- .../settings/@tabs/servers/depart-entity-button.tsx | 5 +++-- .../dashboard/settings/@tabs/servers/entities-list.tsx | 3 +-- .../settings/@tabs/servers/join-entity-button.tsx | 5 +++-- .../settings/@tabs/servers/joinable-entities-list.tsx | 2 +- .../settings/@tabs/servers/joined-entities-list.tsx | 2 +- apps/web/src/lib/auth.ts | 2 +- .../src/services/{auth/actions.ts => actions/auth.ts} | 2 +- .../{commands/actions.ts => actions/commands.ts} | 10 +++++++--- .../{entities/actions.ts => actions/entities.ts} | 2 +- .../{commands/queries.ts => queries/commands.ts} | 4 ++-- .../{entities/queries.ts => queries/entities.ts} | 4 ++-- .../services/{users/queries.ts => queries/users.ts} | 2 +- .../src/services/{auth/schema.ts => schemas/auth.ts} | 4 ++-- .../{commands/schema.ts => schemas/commands.ts} | 10 +++++----- .../services/{entities/schema.ts => schemas/entity.ts} | 6 +++--- apps/web/src/services/schemas/platform.ts | 3 +++ apps/web/src/services/shared/schema.ts | 3 --- apps/web/src/services/shared/type.ts | 5 ----- .../{services/commands/type.ts => types/command.ts} | 2 +- .../src/{services/entities/type.ts => types/entity.ts} | 2 +- apps/web/src/types/platform.ts | 5 +++++ apps/web/src/{services/users/type.ts => types/user.ts} | 2 +- 39 files changed, 74 insertions(+), 63 deletions(-) rename apps/web/src/services/{auth/actions.ts => actions/auth.ts} (92%) rename apps/web/src/services/{commands/actions.ts => actions/commands.ts} (94%) rename apps/web/src/services/{entities/actions.ts => actions/entities.ts} (93%) rename apps/web/src/services/{commands/queries.ts => queries/commands.ts} (85%) rename apps/web/src/services/{entities/queries.ts => queries/entities.ts} (84%) rename apps/web/src/services/{users/queries.ts => queries/users.ts} (92%) rename apps/web/src/services/{auth/schema.ts => schemas/auth.ts} (60%) rename apps/web/src/services/{commands/schema.ts => schemas/commands.ts} (82%) rename apps/web/src/services/{entities/schema.ts => schemas/entity.ts} (73%) create mode 100644 apps/web/src/services/schemas/platform.ts delete mode 100644 apps/web/src/services/shared/schema.ts delete mode 100644 apps/web/src/services/shared/type.ts rename apps/web/src/{services/commands/type.ts => types/command.ts} (82%) rename apps/web/src/{services/entities/type.ts => types/entity.ts} (77%) create mode 100644 apps/web/src/types/platform.ts rename apps/web/src/{services/users/type.ts => types/user.ts} (87%) diff --git a/apps/web/src/app/(auth)/signin/signin-button.tsx b/apps/web/src/app/(auth)/signin/signin-button.tsx index 7696719e..fb80e736 100644 --- a/apps/web/src/app/(auth)/signin/signin-button.tsx +++ b/apps/web/src/app/(auth)/signin/signin-button.tsx @@ -5,7 +5,7 @@ import { useTransition } from 'react' import { Button } from '@/components/ui/button' import { DiscordIcon, LoaderIcon, TwitchIcon } from '@/components/ui/icons' -import { signInWithProvider } from '@/services/auth/actions' +import { signInWithProvider } from '@/services/actions/auth' interface Props { provider: 'twitch' | 'discord' diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx index 69f3dc6f..d890b166 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/command-status-switch.tsx @@ -6,8 +6,9 @@ import { toast } from 'sonner' import { Switch } from '@/components/ui/switch' -import { setCommandStatus } from '@/services/commands/actions' -import type { EntityCommand } from '@/services/commands/type' +import { setCommandStatus } from '@/services/actions/commands' + +import type { EntityCommand } from '@/types/command' interface Props { command: EntityCommand diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx index 5ac9be59..529f19df 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/commands-list.tsx @@ -7,8 +7,9 @@ import { TableRow, } from '@/components/ui/table' -import { getCommands } from '@/services/commands/queries' -import type { Platform } from '@/services/shared/type' +import { getCommands } from '@/services/queries/commands' + +import type { Platform } from '@/types/platform' import { CommandStatusSwitch } from './command-status-switch' import { DeleteCommand } from './delete-command-button' diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx index ab9f7e83..9b40a289 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/custom/page.tsx @@ -3,7 +3,7 @@ import { redirect } from 'next/navigation' import { useSession } from '@/hooks/use-session' -import type { Platform } from '@/services/shared/type' +import type { Platform } from '@/types/platform' import { CommandsList } from '../commands-list' diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx index 3aa36356..02b84f57 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/delete-command-button.tsx @@ -7,8 +7,9 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { deleteCommand } from '@/services/commands/actions' -import type { Platform } from '@/services/shared/type' +import { deleteCommand } from '@/services/actions/commands' + +import type { Platform } from '@/types/platform' interface Props { id: number diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx index ca4e93ad..b9e39787 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/global/page.tsx @@ -3,7 +3,7 @@ import { redirect } from 'next/navigation' import { useSession } from '@/hooks/use-session' -import type { Platform } from '@/services/shared/type' +import type { Platform } from '@/types/platform' import { CommandsList } from '../commands-list' diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx index f45d1770..8538e959 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/layout.tsx @@ -1,6 +1,6 @@ import { TabGroup, TabGroupItem } from '@/components/ui/tab-group' -import type { Platform } from '@/services/shared/type' +import type { Platform } from '@/types/platform' const tabs = [ { diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx index 432a729a..71b277e8 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/@tabs/update-command-dialog.tsx @@ -19,8 +19,9 @@ import { Label } from '@/components/ui/label' import { Link } from '@/components/ui/link' import { Switch } from '@/components/ui/switch' -import { updateCommand } from '@/services/commands/actions' -import type { EntityCommand } from '@/services/commands/type' +import { updateCommand } from '@/services/actions/commands' + +import type { EntityCommand } from '@/types/command' interface Props { command: EntityCommand diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx index 912a552c..fe1c33b4 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/create-command-dialog.tsx @@ -20,8 +20,9 @@ import { Label } from '@/components/ui/label' import { Link } from '@/components/ui/link' import { Switch } from '@/components/ui/switch' -import { createCommand } from '@/services/commands/actions' -import type { Platform } from '@/services/shared/type' +import { createCommand } from '@/services/actions/commands' + +import type { Platform } from '@/types/platform' interface Props { platform: Platform diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx index 5eb4b6e7..d88019e8 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/commands/layout.tsx @@ -1,4 +1,4 @@ -import type { Platform } from '@/services/shared/type' +import type { Platform } from '@/types/platform' import { CreateCommand } from './create-command-dialog' import { ShareCommands } from './share-commands-button' diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx index 0d6d95cb..cdd77fb0 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/entity-logs-card.tsx @@ -3,8 +3,9 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { formatDate } from '@/lib/utils' -import { getEntityLogs } from '@/services/entities/queries' -import type { Platform } from '@/services/shared/type' +import { getEntityLogs } from '@/services/queries/entities' + +import type { Platform } from '@/types/platform' interface Props { platform: Platform diff --git a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx index a72da0e9..f623e383 100644 --- a/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx +++ b/apps/web/src/app/(protected)/dashboard/[platform]/[id]/page.tsx @@ -7,7 +7,7 @@ import { LoaderIcon } from '@/components/ui/icons' import { useSession } from '@/hooks/use-session' -import type { Platform } from '@/services/shared/type' +import type { Platform } from '@/types/platform' import { EntityLogsCard } from './entity-logs-card' diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx index 8765455c..d61ec970 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown-wrapper.tsx @@ -4,7 +4,7 @@ import { PlusIcon } from 'lucide-react' import { Button } from '@/components/ui/button' -import { getUserEntities } from '@/services/users/queries' +import { getUserEntities } from '@/services/queries/users' import { EntitiesDropdown } from './entities-dropdown' diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx index 172a06ae..efd97ff4 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/entities-dropdown.tsx @@ -27,7 +27,7 @@ import { Skeleton } from '@/components/ui/skeleton' import { cn } from '@/lib/utils' -import type { UserEntity } from '@/services/users/type' +import type { UserEntity } from '@/types/user' interface Props { entities: UserEntity[] diff --git a/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx index 7a53e673..30b4292c 100644 --- a/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/joined-entities-list.tsx @@ -3,7 +3,7 @@ import NextLink from 'next/link' import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' import { Link } from '@/components/ui/link' -import { getUserEntities } from '@/services/users/queries' +import { getUserEntities } from '@/services/queries/users' export async function JoinedEntitiesList() { const entities = await getUserEntities('joined') diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx index c4c992a3..d3d2bb10 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/link-account-button.tsx @@ -5,7 +5,7 @@ import { useTransition } from 'react' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { signInWithProvider } from '@/services/auth/actions' +import { signInWithProvider } from '@/services/actions/auth' interface Props { provider: 'twitch' | 'discord' diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx index b3b6ff12..9d273160 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/profile/linked-accounts-list.tsx @@ -4,7 +4,7 @@ import { DiscordIcon, TwitchIcon } from '@/components/ui/icons' import { formatDate } from '@/lib/utils' -import { getUserAccounts } from '@/services/users/queries' +import { getUserAccounts } from '@/services/queries/users' import { LinkAccount } from './link-account-button' diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/depart-entity-button.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/depart-entity-button.tsx index 6062cb12..80527bfa 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/depart-entity-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/depart-entity-button.tsx @@ -8,8 +8,9 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { executeEntityAction } from '@/services/entities/actions' -import type { Platform } from '@/services/shared/type' +import { executeEntityAction } from '@/services/actions/entities' + +import type { Platform } from '@/types/platform' interface Props { platform: Platform diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx index 45d29d51..e67d9ca5 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/entities-list.tsx @@ -1,8 +1,7 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' -import type { UserEntity } from '@/services/users/type' - import { env } from '@/config/env' +import type { UserEntity } from '@/types/user' import { DepartEntity } from './depart-entity-button' import { JoinEntity } from './join-entity-button' diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx index f8e75ed2..3ad69080 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/join-entity-button.tsx @@ -8,8 +8,9 @@ import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { LoaderIcon } from '@/components/ui/icons' -import { executeEntityAction } from '@/services/entities/actions' -import type { Platform } from '@/services/shared/type' +import { executeEntityAction } from '@/services/actions/entities' + +import type { Platform } from '@/types/platform' interface Props { platform: Platform diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx index 52ba2417..add30988 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joinable-entities-list.tsx @@ -1,4 +1,4 @@ -import { getUserEntities } from '@/services/users/queries' +import { getUserEntities } from '@/services/queries/users' import { EntitiesList } from './entities-list' diff --git a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx index 4663c8f2..ab272b55 100644 --- a/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx +++ b/apps/web/src/app/(protected)/dashboard/settings/@tabs/servers/joined-entities-list.tsx @@ -1,4 +1,4 @@ -import { getUserEntities } from '@/services/users/queries' +import { getUserEntities } from '@/services/queries/users' import { EntitiesList } from './entities-list' diff --git a/apps/web/src/lib/auth.ts b/apps/web/src/lib/auth.ts index 35480084..716e0f18 100644 --- a/apps/web/src/lib/auth.ts +++ b/apps/web/src/lib/auth.ts @@ -4,7 +4,7 @@ import Twitch from 'next-auth/providers/twitch' import { PrismaAdapter } from '@auth/prisma-adapter' -import { linkEntity } from '@/services/entities/actions' +import { linkEntity } from '@/services/actions/entities' import { prisma } from './db' diff --git a/apps/web/src/services/auth/actions.ts b/apps/web/src/services/actions/auth.ts similarity index 92% rename from apps/web/src/services/auth/actions.ts rename to apps/web/src/services/actions/auth.ts index 5cd17d23..8cfabed9 100644 --- a/apps/web/src/services/auth/actions.ts +++ b/apps/web/src/services/actions/auth.ts @@ -4,7 +4,7 @@ import { createServerAction } from 'zsa' import { signIn as _signIn, signOut as _signOut } from '@/lib/auth' -import { signInWithProviderSchema } from './schema' +import { signInWithProviderSchema } from '../schemas/auth' /** * diff --git a/apps/web/src/services/commands/actions.ts b/apps/web/src/services/actions/commands.ts similarity index 94% rename from apps/web/src/services/commands/actions.ts rename to apps/web/src/services/actions/commands.ts index 0ad4ca24..39c88c48 100644 --- a/apps/web/src/services/commands/actions.ts +++ b/apps/web/src/services/actions/commands.ts @@ -11,13 +11,15 @@ import { deleteCommandSchema, setCommandStatusSchema, updateCommandSchema, -} from './schema' +} from '../schemas/commands' /** * */ export const createCommand = createServerAction() - .input(createCommandSchema, { type: 'formData' }) + .input(createCommandSchema, { + type: 'formData', + }) .handler(async ({ input }) => { try { const params = new URLSearchParams() @@ -45,7 +47,9 @@ export const createCommand = createServerAction() * */ export const updateCommand = createServerAction() - .input(updateCommandSchema, { type: 'formData' }) + .input(updateCommandSchema, { + type: 'formData', + }) .handler(async ({ input }) => { try { const params = new URLSearchParams() diff --git a/apps/web/src/services/entities/actions.ts b/apps/web/src/services/actions/entities.ts similarity index 93% rename from apps/web/src/services/entities/actions.ts rename to apps/web/src/services/actions/entities.ts index af56b17c..1dfaea47 100644 --- a/apps/web/src/services/entities/actions.ts +++ b/apps/web/src/services/actions/entities.ts @@ -6,7 +6,7 @@ import { ZSAError, createServerAction } from 'zsa' import { fetcher } from '@/lib/fetcher' -import { executeEntityActionSchema, linkEntitySchema } from './schema' +import { executeEntityActionSchema, linkEntitySchema } from '../schemas/entity' /** * diff --git a/apps/web/src/services/commands/queries.ts b/apps/web/src/services/queries/commands.ts similarity index 85% rename from apps/web/src/services/commands/queries.ts rename to apps/web/src/services/queries/commands.ts index 02799a75..12bd2f16 100644 --- a/apps/web/src/services/commands/queries.ts +++ b/apps/web/src/services/queries/commands.ts @@ -1,7 +1,7 @@ import { fetcher } from '@/lib/fetcher' -import type { Platform } from '../shared/type' -import { EntityCommand } from './type' +import type { EntityCommand } from '@/types/command' +import type { Platform } from '@/types/platform' /** * diff --git a/apps/web/src/services/entities/queries.ts b/apps/web/src/services/queries/entities.ts similarity index 84% rename from apps/web/src/services/entities/queries.ts rename to apps/web/src/services/queries/entities.ts index 464a9649..147a67bf 100644 --- a/apps/web/src/services/entities/queries.ts +++ b/apps/web/src/services/queries/entities.ts @@ -1,7 +1,7 @@ import { fetcher } from '@/lib/fetcher' -import type { Platform } from '../shared/type' -import { EntityLog } from './type' +import type { EntityLog } from '@/types/entity' +import type { Platform } from '@/types/platform' /** * diff --git a/apps/web/src/services/users/queries.ts b/apps/web/src/services/queries/users.ts similarity index 92% rename from apps/web/src/services/users/queries.ts rename to apps/web/src/services/queries/users.ts index 5326c100..31e4ce25 100644 --- a/apps/web/src/services/users/queries.ts +++ b/apps/web/src/services/queries/users.ts @@ -1,6 +1,6 @@ import { fetcher } from '@/lib/fetcher' -import type { UserAccount, UserEntity } from './type' +import type { UserAccount, UserEntity } from '@/types/user' /** * diff --git a/apps/web/src/services/auth/schema.ts b/apps/web/src/services/schemas/auth.ts similarity index 60% rename from apps/web/src/services/auth/schema.ts rename to apps/web/src/services/schemas/auth.ts index 197ac46e..c0183ae2 100644 --- a/apps/web/src/services/auth/schema.ts +++ b/apps/web/src/services/schemas/auth.ts @@ -1,8 +1,8 @@ import { z } from 'zod' -import { platformEnum } from '../shared/schema' +import { platform } from './platform' export const signInWithProviderSchema = z.object({ - provider: platformEnum, + provider: platform, redirectTo: z.string().min(1), }) diff --git a/apps/web/src/services/commands/schema.ts b/apps/web/src/services/schemas/commands.ts similarity index 82% rename from apps/web/src/services/commands/schema.ts rename to apps/web/src/services/schemas/commands.ts index a61f89b0..ac0312e3 100644 --- a/apps/web/src/services/commands/schema.ts +++ b/apps/web/src/services/schemas/commands.ts @@ -1,9 +1,9 @@ import { z } from 'zod' -import { platformEnum } from '../shared/schema' +import { platform } from './platform' export const createCommandSchema = z.object({ - platform: platformEnum, + platform: platform, platformEntityId: z.string().min(1), command_name: z .string() @@ -15,7 +15,7 @@ export const createCommandSchema = z.object({ export const updateCommandSchema = z.object({ id: z.number(), - platform: platformEnum, + platform: platform, platformEntityId: z.string().min(1), command_content: z.string().min(1), status: z.coerce.boolean(), @@ -23,13 +23,13 @@ export const updateCommandSchema = z.object({ export const setCommandStatusSchema = z.object({ id: z.number(), - platform: platformEnum, + platform: platform, platformEntityId: z.string().min(1), status: z.coerce.boolean(), }) export const deleteCommandSchema = z.object({ id: z.number(), - platform: platformEnum, + platform: platform, platformEntityId: z.string().min(1), }) diff --git a/apps/web/src/services/entities/schema.ts b/apps/web/src/services/schemas/entity.ts similarity index 73% rename from apps/web/src/services/entities/schema.ts rename to apps/web/src/services/schemas/entity.ts index 86520481..88ba30cb 100644 --- a/apps/web/src/services/entities/schema.ts +++ b/apps/web/src/services/schemas/entity.ts @@ -1,15 +1,15 @@ import { z } from 'zod' -import { platformEnum } from '../shared/schema' +import { platform } from './platform' export const linkEntitySchema = z.object({ - provider: platformEnum, + provider: platform, providerAccountId: z.string().min(1), userId: z.string().min(1), }) export const executeEntityActionSchema = z.object({ action: z.enum(['join', 'depart']), - platform: platformEnum, + platform: platform, platformEntityId: z.string().min(1), }) diff --git a/apps/web/src/services/schemas/platform.ts b/apps/web/src/services/schemas/platform.ts new file mode 100644 index 00000000..20999ed6 --- /dev/null +++ b/apps/web/src/services/schemas/platform.ts @@ -0,0 +1,3 @@ +import { z } from 'zod' + +export const platform = z.enum(['twitch', 'discord']) diff --git a/apps/web/src/services/shared/schema.ts b/apps/web/src/services/shared/schema.ts deleted file mode 100644 index 304696cf..00000000 --- a/apps/web/src/services/shared/schema.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { z } from 'zod' - -export const platformEnum = z.enum(['twitch', 'discord']) diff --git a/apps/web/src/services/shared/type.ts b/apps/web/src/services/shared/type.ts deleted file mode 100644 index 69a51c8e..00000000 --- a/apps/web/src/services/shared/type.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { z } from 'zod' - -import { platformEnum } from './schema' - -export type Platform = z.infer diff --git a/apps/web/src/services/commands/type.ts b/apps/web/src/types/command.ts similarity index 82% rename from apps/web/src/services/commands/type.ts rename to apps/web/src/types/command.ts index d9f51a16..263deadd 100644 --- a/apps/web/src/services/commands/type.ts +++ b/apps/web/src/types/command.ts @@ -1,4 +1,4 @@ -import type { Platform } from '../shared/type' +import type { Platform } from './platform' export type EntityCommand = { id: number diff --git a/apps/web/src/services/entities/type.ts b/apps/web/src/types/entity.ts similarity index 77% rename from apps/web/src/services/entities/type.ts rename to apps/web/src/types/entity.ts index d5962e24..c564a148 100644 --- a/apps/web/src/services/entities/type.ts +++ b/apps/web/src/types/entity.ts @@ -1,4 +1,4 @@ -import type { Platform } from '../shared/type' +import type { Platform } from '@/types/platform' export type EntityLog = { id: string diff --git a/apps/web/src/types/platform.ts b/apps/web/src/types/platform.ts new file mode 100644 index 00000000..d3064b88 --- /dev/null +++ b/apps/web/src/types/platform.ts @@ -0,0 +1,5 @@ +import { z } from 'zod' + +import { platform } from '@/services/schemas/platform' + +export type Platform = z.infer diff --git a/apps/web/src/services/users/type.ts b/apps/web/src/types/user.ts similarity index 87% rename from apps/web/src/services/users/type.ts rename to apps/web/src/types/user.ts index 5569277e..5220a5d9 100644 --- a/apps/web/src/services/users/type.ts +++ b/apps/web/src/types/user.ts @@ -1,4 +1,4 @@ -import type { Platform } from '../shared/type' +import type { Platform } from './platform' export type UserAccount = { user_id: string From cdc5c64640ab45b334c62f3ec5d21145e676a22e Mon Sep 17 00:00:00 2001 From: eckoln Date: Fri, 23 Aug 2024 12:42:26 +0300 Subject: [PATCH 11/16] feat(apps/web): added `event-channels` feature --- apps/web/package.json | 1 + apps/web/pnpm-lock.yaml | 507 +++++++++++++++++- .../dashboard/_sidebar/main-nav.tsx | 12 +- .../create-event-channel-form.tsx | 89 +++ .../event-channels/delete-channel-button.tsx | 43 ++ .../discord/[id]/event-channels/page.tsx | 104 ++++ apps/web/src/components/ui/select.tsx | 165 ++++++ apps/web/src/services/actions/livestreams.ts | 59 ++ apps/web/src/services/queries/discord.ts | 23 + apps/web/src/services/queries/livestreams.ts | 22 + apps/web/src/services/schemas/livestreams.ts | 11 + apps/web/src/types/discord.ts | 6 + apps/web/src/types/livestreams.ts | 7 + 13 files changed, 1028 insertions(+), 21 deletions(-) create mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/delete-channel-button.tsx create mode 100644 apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/page.tsx create mode 100644 apps/web/src/components/ui/select.tsx create mode 100644 apps/web/src/services/actions/livestreams.ts create mode 100644 apps/web/src/services/queries/discord.ts create mode 100644 apps/web/src/services/queries/livestreams.ts create mode 100644 apps/web/src/services/schemas/livestreams.ts create mode 100644 apps/web/src/types/discord.ts create mode 100644 apps/web/src/types/livestreams.ts diff --git a/apps/web/package.json b/apps/web/package.json index 5a144644..a876700d 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -19,6 +19,7 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", "@t3-oss/env-nextjs": "^0.10.1", diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml index 6b020743..2b5c237e 100644 --- a/apps/web/pnpm-lock.yaml +++ b/apps/web/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@radix-ui/react-popover': specifier: ^1.0.7 version: 1.0.7(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-select': + specifier: ^2.1.1 + version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: ^1.0.2 version: 1.0.2(@types/react@18.3.2)(react@18.3.1) @@ -413,9 +416,15 @@ packages: '@prisma/get-platform@5.13.0': resolution: {integrity: sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==} + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/primitive@1.0.1': resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==} + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + '@radix-ui/react-arrow@1.0.3': resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==} peerDependencies: @@ -429,6 +438,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-arrow@1.1.0': + resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-avatar@1.0.4': resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} peerDependencies: @@ -468,6 +490,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.0.1': resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==} peerDependencies: @@ -477,6 +512,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-context@1.0.1': resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==} peerDependencies: @@ -486,6 +530,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dialog@1.0.5': resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==} peerDependencies: @@ -508,6 +561,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-dismissable-layer@1.0.5': resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==} peerDependencies: @@ -521,6 +583,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-dismissable-layer@1.1.0': + resolution: {integrity: sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-dropdown-menu@2.0.6': resolution: {integrity: sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==} peerDependencies: @@ -543,6 +618,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-focus-guards@1.1.0': + resolution: {integrity: sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-focus-scope@1.0.4': resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==} peerDependencies: @@ -556,6 +640,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-focus-scope@1.1.0': + resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-icons@1.3.0': resolution: {integrity: sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==} peerDependencies: @@ -570,6 +667,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-label@2.0.2': resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==} peerDependencies: @@ -622,6 +728,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-popper@1.2.0': + resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-portal@1.0.4': resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==} peerDependencies: @@ -635,6 +754,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-portal@1.1.1': + resolution: {integrity: sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-presence@1.0.1': resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==} peerDependencies: @@ -661,6 +793,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-roving-focus@1.0.4': resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==} peerDependencies: @@ -674,6 +819,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-select@2.1.1': + resolution: {integrity: sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.0.2': resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -683,6 +841,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-switch@1.0.3': resolution: {integrity: sha512-mxm87F88HyHztsI7N+ZUmEoARGkC22YVW5CaC+Byc+HRpuvCrOBPTAnXgf+tZ/7i0Sg/eOePGdMhUKhPaQEqow==} peerDependencies: @@ -705,6 +872,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-controllable-state@1.0.1': resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==} peerDependencies: @@ -714,6 +890,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-escape-keydown@1.0.3': resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==} peerDependencies: @@ -723,6 +908,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-layout-effect@1.0.1': resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==} peerDependencies: @@ -732,6 +926,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-previous@1.0.1': resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==} peerDependencies: @@ -741,6 +944,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.0.1': resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==} peerDependencies: @@ -750,6 +962,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-rect@1.1.0': + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-size@1.0.1': resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==} peerDependencies: @@ -759,9 +980,34 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.0': + resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.0.1': resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==} + '@radix-ui/rect@1.1.0': + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + '@rushstack/eslint-patch@1.10.2': resolution: {integrity: sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==} @@ -2087,6 +2333,16 @@ packages: '@types/react': optional: true + react-remove-scroll@2.5.7: + resolution: {integrity: sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + react-style-singleton@2.2.1: resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} engines: {node: '>=10'} @@ -2712,10 +2968,14 @@ snapshots: dependencies: '@prisma/debug': 5.13.0 + '@radix-ui/number@1.1.0': {} + '@radix-ui/primitive@1.0.1': dependencies: '@babel/runtime': 7.24.5 + '@radix-ui/primitive@1.1.0': {} + '@radix-ui/react-arrow@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2726,6 +2986,15 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-avatar@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2769,6 +3038,18 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2776,6 +3057,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-context@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2783,6 +3070,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-context@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-dialog@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2813,6 +3106,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-direction@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2827,6 +3126,19 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-dismissable-layer@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-dropdown-menu@2.0.6(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2850,6 +3162,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-focus-guards@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2862,6 +3180,17 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-icons@1.3.0(react@18.3.1)': dependencies: react: 18.3.1 @@ -2874,6 +3203,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-id@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-label@2.0.2(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2954,6 +3290,24 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2964,6 +3318,16 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-portal@1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -2985,6 +3349,15 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3003,6 +3376,35 @@ snapshots: '@types/react': 18.3.2 '@types/react-dom': 18.3.0 + '@radix-ui/react-select@2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-context': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-direction': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.2)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + aria-hidden: 1.2.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.5.7(@types/react@18.3.2)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/react-slot@1.0.2(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3011,6 +3413,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-slot@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-switch@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3034,6 +3443,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3042,6 +3457,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3050,6 +3472,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3057,6 +3486,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-previous@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3064,6 +3499,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-rect@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3072,6 +3513,13 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + '@radix-ui/react-use-size@1.0.1(@types/react@18.3.2)(react@18.3.1)': dependencies: '@babel/runtime': 7.24.5 @@ -3080,10 +3528,28 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + '@radix-ui/react-use-size@1.1.0(@types/react@18.3.2)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.2)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.2 + + '@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + '@types/react-dom': 18.3.0 + '@radix-ui/rect@1.0.1': dependencies: '@babel/runtime': 7.24.5 + '@radix-ui/rect@1.1.0': {} + '@rushstack/eslint-patch@1.10.2': {} '@swc/counter@0.1.3': {} @@ -3641,8 +4107,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.8.0(eslint@8.57.0) eslint-plugin-react: 7.34.1(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -3664,13 +4130,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 4.3.4 enhanced-resolve: 5.16.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -3681,28 +4147,18 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 7.9.0(eslint@8.57.0)(typescript@5.4.5) - eslint: 8.57.0 - eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -3712,7 +4168,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.9.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -3723,7 +4179,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.9.0(eslint@8.57.0)(typescript@5.4.5) + '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.4.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -4527,6 +4983,17 @@ snapshots: optionalDependencies: '@types/react': 18.3.2 + react-remove-scroll@2.5.7(@types/react@18.3.2)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.6(@types/react@18.3.2)(react@18.3.1) + react-style-singleton: 2.2.1(@types/react@18.3.2)(react@18.3.1) + tslib: 2.6.2 + use-callback-ref: 1.3.2(@types/react@18.3.2)(react@18.3.1) + use-sidecar: 1.1.2(@types/react@18.3.2)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.2 + react-style-singleton@2.2.1(@types/react@18.3.2)(react@18.3.1): dependencies: get-nonce: 1.0.1 diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx index dbbc27bf..860eb3fd 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react' import { useParams, useSelectedLayoutSegment } from 'next/navigation' -import { HomeIcon, ListIcon } from 'lucide-react' +import { BellIcon, HomeIcon, ListIcon, MegaphoneIcon } from 'lucide-react' import { NavLinkItem } from './nav-link-item' @@ -39,6 +39,16 @@ export function MainNav() { href: `${BASE_URL}/commands`, icon: ListIcon, }, + { + label: 'Event Channels', + href: `${BASE_URL}/event-channels`, + icon: BellIcon, + }, + { + label: 'Announcements', + href: `${BASE_URL}/announcements`, + icon: MegaphoneIcon, + }, ] } else { return [ diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx new file mode 100644 index 00000000..c6a2f515 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx @@ -0,0 +1,89 @@ +'use client' + +import { useState } from 'react' +import { useFormStatus } from 'react-dom' + +import { PlusIcon } from 'lucide-react' +import { toast } from 'sonner' + +import { Button } from '@/components/ui/button' +import { LoaderIcon } from '@/components/ui/icons' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' + +import { createEventChannel } from '@/services/actions/livestreams' + +import type { GuildChannel } from '@/types/discord' + +interface Props { + platformEntityId: string + channels: GuildChannel[] +} + +export function CreateEventChannelForm({ platformEntityId, channels }: Props) { + const [key, setKey] = useState(+new Date()) + + return ( +
{ + formData.append('platformEntityId', platformEntityId) + + const [_, error] = await createEventChannel(formData) + + if (error) { + if (error.code === 'INPUT_PARSE_ERROR') { + return toast.error('Invalid submission!') + } else { + return toast.error(error.message) + } + } + + toast.success('Successfully addded.') + setKey(+new Date()) + }} + > +
+ + +
+ + ) +} + +function SubmitButton({ isDisabled }: { isDisabled?: boolean }) { + const { pending } = useFormStatus() + + return ( + + ) +} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/delete-channel-button.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/delete-channel-button.tsx new file mode 100644 index 00000000..38ce7066 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/delete-channel-button.tsx @@ -0,0 +1,43 @@ +'use client' + +import { useTransition } from 'react' + +import { toast } from 'sonner' + +import { Button } from '@/components/ui/button' +import { LoaderIcon } from '@/components/ui/icons' + +import { deleteEventChannel } from '@/services/actions/livestreams' + +interface Props { + id: string + platformEntityId: string +} + +export function DeleteChannel({ id, platformEntityId }: Props) { + const [pending, startTransition] = useTransition() + + return ( + + ) +} diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/page.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/page.tsx new file mode 100644 index 00000000..c8e38163 --- /dev/null +++ b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/page.tsx @@ -0,0 +1,104 @@ +import type { Metadata } from 'next' +import { redirect } from 'next/navigation' + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from '@/components/ui/table' + +import { formatDate } from '@/lib/utils' + +import { useSession } from '@/hooks/use-session' + +import { getDiscordGuildChannels } from '@/services/queries/discord' +import { getEventChannels } from '@/services/queries/livestreams' + +import { CreateEventChannelForm } from './create-event-channel-form' +import { DeleteChannel } from './delete-channel-button' + +export const metadata: Metadata = { + title: 'Event Channels', +} + +interface Props { + params: { + id: string + } +} + +export default async function Page({ params }: Props) { + const session = await useSession() + + if (!session) { + throw redirect('/signin') + } + + const [guildChannels, eventChannels] = await Promise.all([ + getDiscordGuildChannels(params.id), + getEventChannels(params.id), + ]) + + const filterChannels = guildChannels.filter( + (guildChannel) => + !eventChannels.some( + (eventChannel) => eventChannel.channel_id === guildChannel.id, + ), + ) + + // fixthis + function getEventChannelName(eventChannelId: String) { + return guildChannels.find((channel) => channel.id === eventChannelId)?.name + } + + return ( +
+
+

Event Channels

+

+ Manage your livestream announcements in the specified channels. +

+
+
+
+ +
+
+
+ + + Channel + Created Date + + + + + {eventChannels.map((channel) => ( + + + {getEventChannelName(channel.channel_id)} + + {formatDate(channel.created_at)} + +
+ +
+
+
+ ))} +
+
+
+
+
+ ) +} diff --git a/apps/web/src/components/ui/select.tsx b/apps/web/src/components/ui/select.tsx new file mode 100644 index 00000000..03def281 --- /dev/null +++ b/apps/web/src/components/ui/select.tsx @@ -0,0 +1,165 @@ +'use client' + +import * as React from 'react' + +import { + CaretSortIcon, + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, +} from '@radix-ui/react-icons' +import * as SelectPrimitive from '@radix-ui/react-select' + +import { cn } from '@/lib/utils' + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1', + className, + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = 'popper', ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +} diff --git a/apps/web/src/services/actions/livestreams.ts b/apps/web/src/services/actions/livestreams.ts new file mode 100644 index 00000000..96efdd35 --- /dev/null +++ b/apps/web/src/services/actions/livestreams.ts @@ -0,0 +1,59 @@ +'use server' + +import { revalidateTag } from 'next/cache' + +import { ZSAError, createServerAction } from 'zsa' + +import { fetcher } from '@/lib/fetcher' + +import { + createEventChannelSchema, + deleteEventChannelSchema, +} from '../schemas/livestreams' + +/** + * + */ +export const createEventChannel = createServerAction() + .input(createEventChannelSchema, { + type: 'formData', + }) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platformEntityId', input.platformEntityId) + + await fetcher('/me/livestreams/event-channels?' + params, { + method: 'POST', + body: JSON.stringify({ + guild_channel_id: input.guild_channel_id, + }), + }) + + revalidateTag(`getEventChannels-${input.platformEntityId}`) + } catch (error) { + console.error('createEventChannel =>', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) + +/** + * + */ +export const deleteEventChannel = createServerAction() + .input(deleteEventChannelSchema) + .handler(async ({ input }) => { + try { + const params = new URLSearchParams() + params.append('platformEntityId', input.platformEntityId) + + await fetcher(`/me/livestreams/event-channels/${input.id}?` + params, { + method: 'DELETE', + }) + + revalidateTag(`getEventChannels-${input.platformEntityId}`) + } catch (error) { + console.error('deleteEventChannel =>', error) + throw new ZSAError('ERROR', 'Something went wrong!') + } + }) diff --git a/apps/web/src/services/queries/discord.ts b/apps/web/src/services/queries/discord.ts new file mode 100644 index 00000000..35a4829e --- /dev/null +++ b/apps/web/src/services/queries/discord.ts @@ -0,0 +1,23 @@ +import { fetcher } from '@/lib/fetcher' + +import type { GuildChannel } from '@/types/discord' + +/** + * + * @param platformEntityId + * @returns + */ +export async function getDiscordGuildChannels( + platformEntityId: string, +): Promise { + const params = new URLSearchParams() + params.append('noCache', 'true') + params.append('platformEntityId', platformEntityId) + + return fetcher('/me/discord/guild-channels?' + params, { + cache: 'no-store', + next: { + tags: [`getEventChannels-${platformEntityId}`], + }, + }) +} diff --git a/apps/web/src/services/queries/livestreams.ts b/apps/web/src/services/queries/livestreams.ts new file mode 100644 index 00000000..118400a4 --- /dev/null +++ b/apps/web/src/services/queries/livestreams.ts @@ -0,0 +1,22 @@ +import { fetcher } from '@/lib/fetcher' + +import type { EventChannel } from '@/types/livestreams' + +/** + * + * @param platformEntityId + * @returns + */ +export async function getEventChannels( + platformEntityId: string, +): Promise { + const params = new URLSearchParams() + params.append('noCache', 'true') + params.append('platformEntityId', platformEntityId) + + return fetcher('/me/livestreams/event-channels?' + params, { + next: { + tags: [`getEventChannels-${platformEntityId}`], + }, + }) +} diff --git a/apps/web/src/services/schemas/livestreams.ts b/apps/web/src/services/schemas/livestreams.ts new file mode 100644 index 00000000..c4d2a77e --- /dev/null +++ b/apps/web/src/services/schemas/livestreams.ts @@ -0,0 +1,11 @@ +import { z } from 'zod' + +export const createEventChannelSchema = z.object({ + platformEntityId: z.string(), + guild_channel_id: z.string(), +}) + +export const deleteEventChannelSchema = z.object({ + id: z.string(), + platformEntityId: z.string(), +}) diff --git a/apps/web/src/types/discord.ts b/apps/web/src/types/discord.ts new file mode 100644 index 00000000..54e042b8 --- /dev/null +++ b/apps/web/src/types/discord.ts @@ -0,0 +1,6 @@ +export type GuildChannel = { + id: string + name: string + guild_id: string + type: number +} diff --git a/apps/web/src/types/livestreams.ts b/apps/web/src/types/livestreams.ts new file mode 100644 index 00000000..bf66b486 --- /dev/null +++ b/apps/web/src/types/livestreams.ts @@ -0,0 +1,7 @@ +export type EventChannel = { + id: number + server_id: string + channel_id: string + created_at: string + created_by: string +} From df754adb75b8f38e84804fa4077d9630765d09fc Mon Sep 17 00:00:00 2001 From: eckoln Date: Sun, 25 Aug 2024 20:01:57 +0300 Subject: [PATCH 12/16] feat(apps/web): ui changes for `event-channels` --- .../app/(protected)/dashboard/_sidebar/main-nav.tsx | 9 ++------- .../[id]/event-channels/create-event-channel-form.tsx | 10 +++++----- .../dashboard/discord/[id]/event-channels/page.tsx | 4 ++-- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx b/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx index 860eb3fd..ac7ac281 100644 --- a/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx +++ b/apps/web/src/app/(protected)/dashboard/_sidebar/main-nav.tsx @@ -4,7 +4,7 @@ import { useMemo } from 'react' import { useParams, useSelectedLayoutSegment } from 'next/navigation' -import { BellIcon, HomeIcon, ListIcon, MegaphoneIcon } from 'lucide-react' +import { CalendarIcon, HomeIcon, ListIcon, MegaphoneIcon } from 'lucide-react' import { NavLinkItem } from './nav-link-item' @@ -42,12 +42,7 @@ export function MainNav() { { label: 'Event Channels', href: `${BASE_URL}/event-channels`, - icon: BellIcon, - }, - { - label: 'Announcements', - href: `${BASE_URL}/announcements`, - icon: MegaphoneIcon, + icon: CalendarIcon, }, ] } else { diff --git a/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx index c6a2f515..5cade25b 100644 --- a/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx +++ b/apps/web/src/app/(protected)/dashboard/discord/[id]/event-channels/create-event-channel-form.tsx @@ -47,15 +47,15 @@ export function CreateEventChannelForm({ platformEntityId, channels }: Props) { setKey(+new Date()) }} > -
+