├── .dockerignore ├── .eslintrc.json ├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── Dockerfile ├── README.md ├── auth.ts ├── components.json ├── docs ├── en │ ├── dashboard.png │ ├── login.png │ ├── platform_settings.png │ ├── records.png │ ├── settings.png │ ├── streamers.png │ └── uploads.png └── zh │ ├── dashboard.png │ ├── login.png │ ├── platform_settings.png │ ├── records.png │ ├── settings.png │ ├── streamers.png │ └── uploads.png ├── messages ├── en.json └── zh.json ├── next.config.mjs ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── icons │ ├── mxplayer.svg │ └── potplayer.svg └── stream-rec.svg ├── src ├── app │ ├── [locale] │ │ ├── (auth) │ │ │ ├── layout.tsx │ │ │ ├── login │ │ │ │ ├── login-form.tsx │ │ │ │ └── page.tsx │ │ │ ├── logout │ │ │ │ └── page.tsx │ │ │ └── reset-password │ │ │ │ ├── page.tsx │ │ │ │ └── reset-password-form.tsx │ │ ├── (feat) │ │ │ ├── (stats) │ │ │ │ ├── dashboard-cards.tsx │ │ │ │ ├── stats-card-grid.tsx │ │ │ │ ├── stats-skeleton.tsx │ │ │ │ └── stats.tsx │ │ │ ├── dashboard │ │ │ │ ├── dashboard-layout.tsx │ │ │ │ └── page.tsx │ │ │ ├── error.tsx │ │ │ ├── layout.tsx │ │ │ ├── player │ │ │ │ └── page.tsx │ │ │ ├── playground │ │ │ │ ├── page.tsx │ │ │ │ └── playground-form.tsx │ │ │ ├── records │ │ │ │ ├── [id] │ │ │ │ │ ├── layout.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── components │ │ │ │ │ ├── records-action-colum.tsx │ │ │ │ │ ├── records-details.tsx │ │ │ │ │ ├── records-table-columns.tsx │ │ │ │ │ ├── records-table-toolbar-actions.tsx │ │ │ │ │ ├── records-table-wrapper.tsx │ │ │ │ │ └── records-table.tsx │ │ │ │ └── page.tsx │ │ │ ├── server │ │ │ │ ├── page.tsx │ │ │ │ ├── system-info-cards-skeleton.tsx │ │ │ │ └── system-info-cards.tsx │ │ │ ├── settings │ │ │ │ ├── (global) │ │ │ │ │ ├── global-form-suspense.tsx │ │ │ │ │ ├── global-form.tsx │ │ │ │ │ └── global-setting-skeleton.tsx │ │ │ │ ├── appearance │ │ │ │ │ ├── appearance-form.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── components │ │ │ │ │ ├── form-field-wrapper.tsx │ │ │ │ │ ├── form │ │ │ │ │ │ ├── cookies-formfield.tsx │ │ │ │ │ │ ├── danmu-flag-formfield.tsx │ │ │ │ │ │ ├── duration-input-field.tsx │ │ │ │ │ │ ├── engine-selector.tsx │ │ │ │ │ │ ├── engines │ │ │ │ │ │ │ ├── ffmpeg-based-engine-fields.tsx │ │ │ │ │ │ │ └── kotlin-engine-fields.tsx │ │ │ │ │ │ ├── flag-form-field.tsx │ │ │ │ │ │ ├── form-field.tsx │ │ │ │ │ │ ├── output-file-format-formfield.tsx │ │ │ │ │ │ ├── output-filename-formfield.tsx │ │ │ │ │ │ ├── output-folder-formfield.tsx │ │ │ │ │ │ └── size-input-field.tsx │ │ │ │ │ ├── pages.tsx │ │ │ │ │ ├── sidebar-nav.tsx │ │ │ │ │ └── warning-hover-card.tsx │ │ │ │ ├── layout.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── platform │ │ │ │ │ ├── page.tsx │ │ │ │ │ ├── platform-form-skeleton.tsx │ │ │ │ │ ├── platform-form-suspense.tsx │ │ │ │ │ ├── platform-form-wrapper.tsx │ │ │ │ │ ├── platform-form.tsx │ │ │ │ │ └── tabs │ │ │ │ │ │ ├── base-download-tab.tsx │ │ │ │ │ │ ├── common-platform-tab.tsx │ │ │ │ │ │ ├── douyin-tab.tsx │ │ │ │ │ │ ├── douyu-tab.tsx │ │ │ │ │ │ ├── huya-tab.tsx │ │ │ │ │ │ ├── pandatv-tab.tsx │ │ │ │ │ │ ├── twitch-tab.tsx │ │ │ │ │ │ └── weibo-tab.tsx │ │ │ │ └── web │ │ │ │ │ ├── page.tsx │ │ │ │ │ └── web-form.tsx │ │ │ ├── streamers │ │ │ │ ├── [id] │ │ │ │ │ └── edit │ │ │ │ │ │ ├── edit-form-wrapper.tsx │ │ │ │ │ │ ├── layout.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ ├── components │ │ │ │ │ ├── account-switcher.tsx │ │ │ │ │ ├── actions │ │ │ │ │ │ ├── action-card.tsx │ │ │ │ │ │ ├── actions-callback-tab.tsx │ │ │ │ │ │ ├── form │ │ │ │ │ │ │ ├── command-form.tsx │ │ │ │ │ │ │ ├── copy-form.tsx │ │ │ │ │ │ │ ├── delete-action-form.tsx │ │ │ │ │ │ │ ├── move-form.tsx │ │ │ │ │ │ │ └── rclone-form.tsx │ │ │ │ │ │ └── new-action-dialog.tsx │ │ │ │ │ ├── open-video-context-menu.tsx │ │ │ │ │ ├── platform-registry.tsx │ │ │ │ │ ├── platforms │ │ │ │ │ │ ├── base-download-config.tsx │ │ │ │ │ │ ├── douyin-platform.tsx │ │ │ │ │ │ ├── douyu-platform.tsx │ │ │ │ │ │ ├── huya-platform.tsx │ │ │ │ │ │ ├── pandalive-platform.tsx │ │ │ │ │ │ ├── twitch-platform.tsx │ │ │ │ │ │ └── weibo-platform.tsx │ │ │ │ │ ├── record-list-skeleton.tsx │ │ │ │ │ ├── record-list.tsx │ │ │ │ │ ├── server-record-list.tsx │ │ │ │ │ ├── streamer-card.tsx │ │ │ │ │ ├── streamer-form-wrapper.tsx │ │ │ │ │ ├── streamer-form.tsx │ │ │ │ │ ├── streamer-grid-list.tsx │ │ │ │ │ ├── streamer-hover-card.tsx │ │ │ │ │ ├── streamer-list-wrapper.tsx │ │ │ │ │ ├── streamer-list.tsx │ │ │ │ │ ├── streamer-skeleton.tsx │ │ │ │ │ ├── time-selector.tsx │ │ │ │ │ └── timer-picker.tsx │ │ │ │ ├── new │ │ │ │ │ ├── layout.tsx │ │ │ │ │ ├── new-form-wrapper.tsx │ │ │ │ │ └── page.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── utils │ │ │ │ │ └── streamer-utils.tsx │ │ │ └── uploads │ │ │ │ ├── [id] │ │ │ │ ├── layout.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── upload-details-wrapper.tsx │ │ │ │ ├── components │ │ │ │ ├── upload-action-column.tsx │ │ │ │ ├── upload-details.tsx │ │ │ │ ├── upload-table-columns.tsx │ │ │ │ ├── upload-table-toolbar-actions.tsx │ │ │ │ ├── upload-table-wrapper.tsx │ │ │ │ └── upload-table.tsx │ │ │ │ └── page.tsx │ │ ├── error │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ └── page.tsx │ ├── api │ │ ├── auth │ │ │ └── [...nextauth] │ │ │ │ └── route.ts │ │ └── proxy │ │ │ └── route.ts │ ├── components │ │ ├── dialog │ │ │ ├── delete-icon-dialog.tsx │ │ │ └── delete-items-dialog.tsx │ │ ├── empty-select.tsx │ │ └── table │ │ │ ├── advanced │ │ │ ├── data-table-advanced-faceted-filter.tsx │ │ │ ├── data-table-advanced-toolbar.tsx │ │ │ ├── data-table-filter-combobox.tsx │ │ │ ├── data-table-filter-item.tsx │ │ │ └── data-table-multi-filter.tsx │ │ │ ├── config │ │ │ └── data-table.ts │ │ │ ├── data-table-column-header.tsx │ │ │ ├── data-table-date-range-picker.tsx │ │ │ ├── data-table-datepicker-filter.tsx │ │ │ ├── data-table-faceted-filter.tsx │ │ │ ├── data-table-pagination.tsx │ │ │ ├── data-table-skeleton.tsx │ │ │ ├── data-table-toolbar.tsx │ │ │ ├── data-table-view-options.tsx │ │ │ └── data-table.tsx │ ├── globals.css │ ├── hooks │ │ ├── translations │ │ │ ├── base-global-platform-translation.tsx │ │ │ ├── douyin-translations.tsx │ │ │ ├── douyu-translations.tsx │ │ │ ├── engine-translations.tsx │ │ │ ├── global-settings-translations.tsx │ │ │ ├── huya-translations.tsx │ │ │ ├── pandatv-translations.tsx │ │ │ ├── twitch-translations.tsx │ │ │ ├── use-server-info-translations.tsx │ │ │ ├── use-sidebar-translations.tsx │ │ │ └── weibo-translations.tsx │ │ ├── use-config.ts │ │ ├── use-data-table.ts │ │ ├── use-debounce.ts │ │ ├── use-media-query.ts │ │ ├── use-sidebar-toggle.ts │ │ └── use-store.ts │ ├── layout.tsx │ ├── not-found.tsx │ ├── page.tsx │ └── utils │ │ ├── conversions.ts │ │ └── toast.tsx ├── components │ ├── Logo.tsx │ ├── dashboard │ │ ├── collapse-menu-button.tsx │ │ ├── content-layout.tsx │ │ ├── footer.tsx │ │ ├── menu.tsx │ │ ├── navbar.tsx │ │ ├── sheet-menu.tsx │ │ ├── sidebar-toggle.tsx │ │ ├── sidebar.tsx │ │ └── user-nav.tsx │ ├── dialog │ │ └── error-dialog.tsx │ ├── i18n │ │ └── RichText.tsx │ ├── new-york │ │ └── ui │ │ │ ├── accordion.tsx │ │ │ ├── alert-dialog.tsx │ │ │ ├── alert.tsx │ │ │ ├── aspect-ratio.tsx │ │ │ ├── autosize-textarea.tsx │ │ │ ├── avatar.tsx │ │ │ ├── badge.tsx │ │ │ ├── breadcrumb.tsx │ │ │ ├── button.tsx │ │ │ ├── calendar.tsx │ │ │ ├── card.tsx │ │ │ ├── checkbox.tsx │ │ │ ├── collapsible.tsx │ │ │ ├── command.tsx │ │ │ ├── context-menu.tsx │ │ │ ├── dialog.tsx │ │ │ ├── drawer.tsx │ │ │ ├── dropdown-menu.tsx │ │ │ ├── form.tsx │ │ │ ├── hover-card.tsx │ │ │ ├── input.tsx │ │ │ ├── label.tsx │ │ │ ├── loading-button.tsx │ │ │ ├── loading-skeleton.tsx │ │ │ ├── pagination.tsx │ │ │ ├── popover.tsx │ │ │ ├── radio-group.tsx │ │ │ ├── resizable.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── select.tsx │ │ │ ├── separator.tsx │ │ │ ├── sheet.tsx │ │ │ ├── skeleton.tsx │ │ │ ├── sonner.tsx │ │ │ ├── switch.tsx │ │ │ ├── table.tsx │ │ │ ├── tabs.tsx │ │ │ ├── textarea.tsx │ │ │ ├── timer-picker-input.tsx │ │ │ └── tooltip.tsx │ ├── session │ │ └── SessionProvider.tsx │ ├── theme │ │ ├── mode-toggle.tsx │ │ ├── styles.tsx │ │ ├── theme-provider.tsx │ │ └── themes.tsx │ └── utils │ │ └── timer-picker-utils.tsx ├── i18n │ ├── request.ts │ └── routing.ts ├── lib │ ├── data │ │ ├── actions │ │ │ └── definitions.ts │ │ ├── api.ts │ │ ├── config │ │ │ ├── apis.ts │ │ │ └── definitions.ts │ │ ├── definitions.ts │ │ ├── engines │ │ │ ├── definitions.ts │ │ │ └── engines-apis.ts │ │ ├── events │ │ │ ├── definitions.ts │ │ │ └── events-api.ts │ │ ├── files │ │ │ ├── definitions.ts │ │ │ └── files-api.ts │ │ ├── mediainfo │ │ │ ├── definitions.ts │ │ │ └── extractor-apis.ts │ │ ├── placeholder-data.js │ │ ├── platform │ │ │ ├── definitions.ts │ │ │ ├── douyin │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ │ ├── douyu │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ │ ├── huya │ │ │ │ ├── apis.ts │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ │ ├── pandatv │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ │ ├── twitch │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ │ └── weibo │ │ │ │ ├── constants.ts │ │ │ │ └── definitions.ts │ │ ├── server │ │ │ ├── api.ts │ │ │ └── definitions.ts │ │ ├── stats │ │ │ ├── api.ts │ │ │ └── definitions.ts │ │ ├── streams │ │ │ ├── definitions.ts │ │ │ ├── stream-apis.ts │ │ │ └── streamer-apis.ts │ │ ├── uploads │ │ │ ├── definitions.ts │ │ │ └── upload-apis.ts │ │ ├── user │ │ │ ├── definitions.ts │ │ │ └── user-apis.ts │ │ └── websocket │ │ │ └── definitions.ts │ ├── errors │ │ └── api-error.ts │ ├── menu-list.ts │ ├── routes.ts │ ├── stores │ │ ├── live-stream-store.ts │ │ └── player-store.ts │ ├── utils.ts │ ├── utils │ │ ├── proxy.ts │ │ └── stream-player.ts │ └── version.ts ├── middleware.ts └── types │ ├── next-auth.d.ts │ └── table.ts ├── tailwind.config.ts └── tsconfig.json /.dockerignore: -------------------------------------------------------------------------------- 1 | # add git-ignore syntax here of things you don't want copied into docker image 2 | 3 | .results 4 | scripts 5 | *Dockerfile* 6 | node_modules 7 | .env -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next", "prettier"] 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | build 4 | dist 5 | coverage -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "trailingComma": "es5", 4 | "singleQuote": false, 5 | "tabWidth": 2, 6 | "useTabs": true, 7 | "printWidth": 120, 8 | "bracketSpacing": true, 9 | "arrowParens": "avoid", 10 | "endOfLine": "lf", 11 | "bracketSameLine": false, 12 | "jsxSingleQuote": true, 13 | "plugins": ["prettier-plugin-tailwindcss"] 14 | } 15 | -------------------------------------------------------------------------------- /components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "@/src/app/globals.css", 9 | "baseColor": "slate", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/src/components/new-york", 15 | "utils": "@/src/lib/utils" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docs/en/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/dashboard.png -------------------------------------------------------------------------------- /docs/en/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/login.png -------------------------------------------------------------------------------- /docs/en/platform_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/platform_settings.png -------------------------------------------------------------------------------- /docs/en/records.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/records.png -------------------------------------------------------------------------------- /docs/en/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/settings.png -------------------------------------------------------------------------------- /docs/en/streamers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/streamers.png -------------------------------------------------------------------------------- /docs/en/uploads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/en/uploads.png -------------------------------------------------------------------------------- /docs/zh/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/dashboard.png -------------------------------------------------------------------------------- /docs/zh/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/login.png -------------------------------------------------------------------------------- /docs/zh/platform_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/platform_settings.png -------------------------------------------------------------------------------- /docs/zh/records.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/records.png -------------------------------------------------------------------------------- /docs/zh/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/settings.png -------------------------------------------------------------------------------- /docs/zh/streamers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/streamers.png -------------------------------------------------------------------------------- /docs/zh/uploads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stream-rec/stream-rec-frontend/cd3befe277d20423697761f2a97fb988da7d8328/docs/zh/uploads.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | import createNextIntlPlugin from "next-intl/plugin" 2 | import { readFileSync } from "fs" 3 | import { join } from "path" 4 | import { execSync } from "node:child_process" 5 | 6 | const withNextIntl = createNextIntlPlugin() 7 | 8 | // current git tag version 9 | let gitVersion 10 | try { 11 | gitVersion = execSync("git describe --tags --always --first-parent").toString().trim() 12 | } catch (error) { 13 | console.error("Error while extracting git version, parsing from package.json", error) 14 | const packageJsonPath = join(process.cwd(), "package.json") 15 | const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8")) 16 | gitVersion = packageJson.version 17 | } 18 | 19 | let appVersion = gitVersion 20 | 21 | /** @type {import('next').NextConfig} */ 22 | const nextConfig = { 23 | async redirects() { 24 | return [ 25 | { 26 | source: "/", 27 | destination: "/dashboard", 28 | permanent: true, 29 | }, 30 | ] 31 | }, 32 | basePath: process.env.NEXT_PUBLIC_BASE_PATH, 33 | output: "standalone", 34 | env: { 35 | APP_VERSION: appVersion, 36 | MIN_SERVER_VERSION: "10565", 37 | AUTH_URL: process.env.NEXT_PUBLIC_BASE_URL, 38 | AUTH_TRUST_HOST: process.env.AUTH_TRUST_HOST, 39 | }, 40 | } 41 | 42 | export default withNextIntl(nextConfig) 43 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/stream-rec.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /src/app/[locale]/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react" 2 | 3 | export default function AuthLayout({ children }: { children: Readonly }) { 4 | return
{children}
5 | } 6 | -------------------------------------------------------------------------------- /src/app/[locale]/(auth)/login/page.tsx: -------------------------------------------------------------------------------- 1 | import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/src/components/new-york/ui/card" 2 | import { getTranslations } from "next-intl/server" 3 | import { redirect } from "next/navigation" 4 | import { cookies } from "next/headers" 5 | import { LoginForm } from "./login-form" 6 | import { auth } from "@/auth" 7 | import { Metadata } from "next" 8 | 9 | export const metadata: Metadata = { 10 | title: "Login", 11 | description: "Login to your account" 12 | } 13 | 14 | export const dynamic = "force-dynamic" 15 | 16 | export default async function LoginPage() { 17 | const session = await auth() 18 | 19 | // Redirect logged in users 20 | if (session) { 21 | if (session.user?.isFirstUsePassword) { 22 | redirect(`/reset-password`) 23 | } else { 24 | redirect("/dashboard") 25 | } 26 | } 27 | 28 | // Get the username from cookie 29 | const cookieStore = await cookies() 30 | const savedUsername = cookieStore.get("username")?.value || "" 31 | 32 | const defaultValues = { 33 | id: 0, 34 | username: savedUsername, 35 | password: "", 36 | } 37 | 38 | const t = await getTranslations("LoginPage") 39 | 40 | const loginStrings = { 41 | username: t("username"), 42 | usernamePlaceholder: t("usernamePlaceholder"), 43 | password: t("password"), 44 | passwordPlaceholder: t("passwordPlaceholder"), 45 | rememberMe: t("rememberMe"), 46 | forgotPassword: t("forgotPassword"), 47 | recoverPasswordSuccess: t("recoverPasswordSuccess"), 48 | signIn: t("signIn"), 49 | loginSuccessful: t("loginSuccess"), 50 | loginFailed: t("loginError"), 51 | } 52 | 53 | return ( 54 |
55 | 56 | 57 | {t("title")} 58 | {t("description")} 59 | 60 | 61 | 62 | 63 | 64 |
65 | ) 66 | } 67 | -------------------------------------------------------------------------------- /src/app/[locale]/(auth)/logout/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import { use, useEffect } from "react" 3 | import { signOut } from "next-auth/react" 4 | import { BASE_PATH } from "@/src/lib/routes" 5 | import { Loader2 } from "lucide-react" 6 | import { useRouter } from "@/src/i18n/routing" 7 | 8 | export default function LogoutPage({ params }: { params: Promise<{ locale: string }> }) { 9 | const { locale } = use(params) 10 | 11 | const router = useRouter() 12 | 13 | useEffect(() => { 14 | // Sign out 15 | signOut({ callbackUrl: `${BASE_PATH}/${locale}/login`, redirect: false }) 16 | .then(() => { 17 | console.log("Logged out...") 18 | router.push(`${BASE_PATH}/login`) 19 | }) 20 | // eslint-disable-next-line react-hooks/exhaustive-deps 21 | }, []) 22 | 23 | return ( 24 |
25 | 26 |

Logging out...

27 |
28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /src/app/[locale]/(auth)/reset-password/page.tsx: -------------------------------------------------------------------------------- 1 | import { getTranslations } from "next-intl/server" 2 | import { Card, CardContent, CardHeader, CardTitle } from "@/src/components/new-york/ui/card" 3 | import { ResetPasswordForm } from "@/src/app/[locale]/(auth)/reset-password/reset-password-form" 4 | import { cookies } from "next/headers" 5 | import { auth } from "@/auth" 6 | 7 | export default async function ResetPage() { 8 | const session = await auth() 9 | 10 | const t = await getTranslations("ResetPasswordPage") 11 | 12 | const strings = { 13 | password: t("currentPassword"), 14 | passwordPlaceholder: t("currentPasswordPlaceholder"), 15 | newPassword: t("newPassword"), 16 | newPasswordPlaceholder: t("newPasswordPlaceholder"), 17 | confirmPassword: t("confirmPassword"), 18 | confirmPasswordPlaceholder: t("confirmPasswordPlaceholder"), 19 | confirm: t("confirm"), 20 | resetPasswordSuccess: t("resetPasswordSuccess"), 21 | resetPasswordError: t("resetPasswordError"), 22 | passwordNotMatch: t("passwordNotMatch"), 23 | } 24 | 25 | const id = session?.user?.id 26 | 27 | const cookieStore = await cookies() 28 | 29 | const defaultValues = { 30 | id: id ? Number(id) : 0, 31 | username: cookieStore.get("username")?.value || "", 32 | password: "", 33 | newPassword: "", 34 | } 35 | 36 | return ( 37 | <> 38 |
39 | 40 | 41 | {t("title")} 42 | 43 | 44 | 45 | 46 | 47 |
48 | 49 | ) 50 | } 51 | -------------------------------------------------------------------------------- /src/app/[locale]/(feat)/(stats)/stats-card-grid.tsx: -------------------------------------------------------------------------------- 1 | import React, { Suspense } from "react" 2 | import StatsSkeleton from "@/src/app/[locale]/(feat)/(stats)/stats-skeleton" 3 | import { TotalStatsCard, WeeklyStatsCard } from "@/src/app/[locale]/(feat)/(stats)/dashboard-cards" 4 | 5 | export default function StatsCardGrid() { 6 | return ( 7 |
8 |
9 | }> 10 | 11 | 12 |
13 |
14 | }> 15 | 16 | 17 |
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /src/app/[locale]/(feat)/(stats)/stats-skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@/src/components/new-york/ui/skeleton" 2 | 3 | export default function StatsSkeleton() { 4 | return ( 5 |
6 | 7 | 8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/app/[locale]/(feat)/dashboard/dashboard-layout.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import * as React from "react" 3 | import { cn } from "@/src/lib/utils" 4 | import { Sidebar } from "@/src/components/dashboard/sidebar" 5 | import { Footer } from "@/src/components/dashboard/footer" 6 | import { useStore } from "@/src/app/hooks/use-store" 7 | import { useSidebarToggle } from "@/src/app/hooks/use-sidebar-toggle" 8 | import { SidebarStrings } from "@/src/app/hooks/translations/use-sidebar-translations" 9 | 10 | interface DashboardProps { 11 | accounts: { 12 | label: string 13 | email: string 14 | icon: React.ReactNode 15 | }[] 16 | strings: SidebarStrings 17 | children?: React.ReactNode 18 | } 19 | 20 | export function DashboardLayout({ accounts, strings, children }: DashboardProps) { 21 | const sidebar = useStore(useSidebarToggle, state => state) 22 | 23 | if (!sidebar) return null 24 | 25 | return ( 26 | <> 27 | 28 |
34 | {children} 35 |
36 |