feat: migrate styles to Tailwind CSS, remove unused files, and add new layout components
This commit is contained in:
parent
5b92932d82
commit
63941e41c3
94 changed files with 1051 additions and 2851 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -23,3 +23,5 @@ dist-ssr
|
|||
*.sln
|
||||
*.sw?
|
||||
output.sh
|
||||
.next
|
||||
ui
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React, { useState } from 'react';
|
||||
import { UserAuthForm } from './components/user-auth-form';
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React from 'react';
|
||||
import { MessageList } from './message-list';
|
||||
import { ChatInput } from './chat-input';
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import React from 'react';
|
||||
import { VideoPlayer } from './video-player';
|
||||
import { ImageViewer } from './image-viewer';
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React from 'react';
|
||||
import { useChat } from '../../providers/chat-provider';
|
||||
import '../../styles/selector.css';
|
|
@ -1,5 +1,6 @@
|
|||
"use client";
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { soundAssets } from '../../../public/sounds/manifest';
|
||||
import { soundAssets } from '../../../../public/sounds/manifest';
|
||||
//import { cacheAssets } from '../lib/asset-loader';
|
||||
|
||||
export function SoundInitializer({ children }: { children: React.ReactNode }) {
|
|
@ -4,7 +4,7 @@ import { ChatLayout } from './components/chat-layout';
|
|||
import { SoundInitializer } from './components/sound-initializer';
|
||||
import { SoundProvider } from './providers/sound-provider';
|
||||
|
||||
export function Chat() {
|
||||
export default function Chat() {
|
||||
return (
|
||||
<SoundInitializer>
|
||||
<SoundProvider>
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { core } from '@tauri-apps/api';
|
||||
import { User, ChatInstance } from '../types';
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React, { createContext, useContext, useCallback } from 'react';
|
||||
import { core } from '@tauri-apps/api';
|
||||
|
36
app/client-nav.tsx
Normal file
36
app/client-nav.tsx
Normal file
|
@ -0,0 +1,36 @@
|
|||
"use client";
|
||||
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { Button } from '../src/components/ui/button';
|
||||
|
||||
const examples = [
|
||||
{ name: "Home", href: "/authentication" },
|
||||
{ name: "Dashboard", href: "/dashboard" },
|
||||
{ name: "Chat", href: "/chat" },
|
||||
{ name: "Mail", href: "/mail" },
|
||||
{ name: "Drive", href: "/drive" },
|
||||
{ name: "Tasks", href: "/tasks" },
|
||||
{ name: "Templates", href: "/templates" },
|
||||
{ name: "Settings", href: "/sync" },
|
||||
];
|
||||
|
||||
export function Nav() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<div className="examples-nav-container">
|
||||
<div className="examples-nav-inner">
|
||||
{examples.map((example) => (
|
||||
<Button
|
||||
key={example.href}
|
||||
onClick={() => router.push(example.href)}
|
||||
className={`example-button ${pathname === example.href ? 'active' : ''}`}
|
||||
>
|
||||
{example.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import React, { useState } from 'react';
|
||||
import { format } from 'date-fns';
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
const groups = [
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export function UserNav() {
|
|
@ -1,9 +1,10 @@
|
|||
"use client";
|
||||
import { useState } from 'react';
|
||||
import { FileTree } from './components/FileTree';
|
||||
import { FileBrowser } from './components/FileBrowser';
|
||||
import { FileOperations } from './components/FileOperations';
|
||||
|
||||
export function DriveScreen() {
|
||||
export default function DriveScreen() {
|
||||
const [currentPath, setCurrentPath] = useState('');
|
||||
const [refreshKey, setRefreshKey] = useState(0);
|
||||
|
17
app/layout.tsx
Normal file
17
app/layout.tsx
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Nav } from './client-nav';
|
||||
import './globals.css';
|
||||
|
||||
import './globals.css' // This path is correct if the file is in your src/app directory
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body>
|
||||
<Nav />
|
||||
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
|
@ -42,7 +42,7 @@ import {
|
|||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
import { Mail } from "@/mail/data"
|
||||
import { Mail } from "./data"
|
||||
|
||||
interface MailDisplayProps {
|
||||
mail: Mail | null
|
|
@ -4,8 +4,8 @@ import formatDistanceToNow from "date-fns/formatDistanceToNow"
|
|||
import { cn } from "@/lib/utils"
|
||||
import { Badge } from "@/components/ui/badge"
|
||||
import { ScrollArea } from "@/components/ui/scroll-area"
|
||||
import { Mail } from "@/mail/data"
|
||||
import { useMail } from "@/mail/use-mail"
|
||||
import { Mail } from "../data"
|
||||
import { useMail } from "../use-mail"
|
||||
|
||||
interface MailListProps {
|
||||
items: Mail[]
|
|
@ -30,12 +30,12 @@ import {
|
|||
TabsTrigger,
|
||||
} from "@/components/ui/tabs"
|
||||
import { TooltipProvider } from "@/components/ui/tooltip"
|
||||
import { AccountSwitcher } from "@/mail/components/account-switcher"
|
||||
import { MailDisplay } from "@/mail/components/mail-display"
|
||||
import { MailList } from "@/mail/components/mail-list"
|
||||
import { Nav } from "@/mail/components/nav"
|
||||
import { type Mail } from "@/mail/data"
|
||||
import { useMail } from "@/mail/use-mail"
|
||||
import { AccountSwitcher } from "./account-switcher"
|
||||
import { MailDisplay } from "./mail-display"
|
||||
import { MailList } from "./mail-list"
|
||||
import { Nav } from "./nav"
|
||||
import { type Mail } from "../data"
|
||||
import { useMail } from "../use-mail"
|
||||
|
||||
interface MailProps {
|
||||
accounts: {
|
|
@ -1,6 +1,7 @@
|
|||
"use client"
|
||||
|
||||
import { Link } from 'lucide-react';
|
||||
import NextLink from 'next/link'; // Changed from lucide-react
|
||||
import { Link as LinkIcon } from 'lucide-react'; // Renamed to avoid conflict
|
||||
import { LucideIcon } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
@ -32,7 +33,7 @@ export function Nav({ links, isCollapsed }: NavProps) {
|
|||
isCollapsed ? (
|
||||
<Tooltip key={index} delayDuration={0}>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
<NextLink
|
||||
href="#"
|
||||
className={cn(
|
||||
buttonVariants({ variant: link.variant, size: "icon" }),
|
||||
|
@ -43,7 +44,7 @@ export function Nav({ links, isCollapsed }: NavProps) {
|
|||
>
|
||||
<link.icon className="h-4 w-4" />
|
||||
<span className="sr-only">{link.title}</span>
|
||||
</Link>
|
||||
</NextLink>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="flex items-center gap-4">
|
||||
{link.title}
|
||||
|
@ -55,7 +56,7 @@ export function Nav({ links, isCollapsed }: NavProps) {
|
|||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Link
|
||||
<NextLink
|
||||
key={index}
|
||||
href="#"
|
||||
className={cn(
|
||||
|
@ -78,7 +79,7 @@ export function Nav({ links, isCollapsed }: NavProps) {
|
|||
{link.label}
|
||||
</span>
|
||||
)}
|
||||
</Link>
|
||||
</NextLink>
|
||||
)
|
||||
)}
|
||||
</nav>
|
27
app/mail/page.tsx
Normal file
27
app/mail/page.tsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
import Image from "next/image"
|
||||
|
||||
import { Mail } from "./components/mail"
|
||||
import { accounts, mails } from "./data"
|
||||
|
||||
export default function MailPage() {
|
||||
const layout = [20, 32, 48]; //cookies().get("react-resizable-panels:layout:mail")
|
||||
const collapsed = false; //cookies().get("react-resizable-panels:collapsed")
|
||||
|
||||
const defaultLayout = layout// ? JSON.parse(layout.value) : undefined
|
||||
const defaultCollapsed = collapsed //? JSON.parse(collapsed.value) : undefined
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex-col md:flex">
|
||||
<Mail
|
||||
accounts={accounts}
|
||||
mails={mails}
|
||||
defaultLayout={defaultLayout}
|
||||
defaultCollapsed={defaultCollapsed}
|
||||
navCollapsedSize={4}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
import { atom, useAtom } from "jotai"
|
||||
|
||||
import { Mail, mails } from "@/mail/data"
|
||||
import { Mail, mails } from "./data"
|
||||
|
||||
type Config = {
|
||||
selected: Mail["id"] | null
|
9
app/page.tsx
Normal file
9
app/page.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
// app/page.tsx (your home page)
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="min-h-screen p-8">
|
||||
<h1 className="text-3xl font-bold">Welcome to My Tauri App</h1>
|
||||
<p className="mt-4">This is your home page</p>
|
||||
</main>
|
||||
)
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
"use client";
|
||||
import { useState, useEffect } from "react";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Task } from '../data/schema';
|
||||
import { labels, priorities, statuses } from '../data/data';
|
|
@ -1,7 +0,0 @@
|
|||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
|
||||
plugins: ['nativewind/babel']
|
||||
};
|
||||
};
|
12
gbclient.code-workspace
Normal file
12
gbclient.code-workspace
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"name": "ui",
|
||||
"path": "../../ui"
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
5
next-env.d.ts
vendored
Normal file
5
next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
7
next.config.js
Normal file
7
next.config.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: 'export',
|
||||
images: { unoptimized: true }
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
1023
package-lock.json
generated
1023
package-lock.json
generated
File diff suppressed because it is too large
Load diff
20
package.json
20
package.json
|
@ -2,16 +2,15 @@
|
|||
"name": "gbclient",
|
||||
"private": true,
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "npx tailwindcss -i ./src/styles/globals.css -o ./public/output.css;ESBUILD_RUNNER=true vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
"dev": "npx tailwindcss -i ./src/styles/globals.css -o ./public/output.css;ESBUILD_RUNNER=true next dev",
|
||||
"build": "tsc && next build",
|
||||
"start": "next start",
|
||||
"tauri": "tauri"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.26.0",
|
||||
"@hookform/resolvers": "^3.9.1",
|
||||
"@next/font": "^14.2.15",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.6",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.2",
|
||||
|
@ -52,6 +51,7 @@
|
|||
"jotai": "^2.12.2",
|
||||
"lucide-react": "0.454.0",
|
||||
"nativewind": "2.0.10",
|
||||
"next": "^15.2.4",
|
||||
"postcss": "8.4.35",
|
||||
"react": "18.3.1",
|
||||
"react-day-picker": "^8.10.1",
|
||||
|
@ -59,21 +59,18 @@
|
|||
"react-hook-form": "^7.53.2",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-router-dom": "7.4.1",
|
||||
"tailwind-merge": "3.0.2",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
"uuid": "11.0.3",
|
||||
"zod": "^3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.18.6",
|
||||
"@tauri-apps/cli": "2",
|
||||
"@types/jest": "29.5.12",
|
||||
"@types/node": "22.13.14",
|
||||
"@types/react": "18.3.1",
|
||||
"@types/react-dom": "18.3.1",
|
||||
"@types/react": "^18.3.1",
|
||||
"@types/react-dom": "^18.3.1",
|
||||
"@types/react-test-renderer": "18.0.7",
|
||||
"@vitejs/plugin-react": "4.3.4",
|
||||
"copy-webpack-plugin": "12.0.2",
|
||||
"esbuild-runner": "2.2.2",
|
||||
"jest": "29.2.1",
|
||||
|
@ -81,7 +78,6 @@
|
|||
"postcss-load-config": "6.0.1",
|
||||
"react-test-renderer": "18.2.0",
|
||||
"tailwindcss": "3.1.8",
|
||||
"typescript": "5.6.2",
|
||||
"vite": "6.0.3"
|
||||
"typescript": "5.6.2"
|
||||
}
|
||||
}
|
||||
|
|
2296
public/output.css
2296
public/output.css
File diff suppressed because it is too large
Load diff
|
@ -4,11 +4,10 @@
|
|||
"version": "6.0.0",
|
||||
"identifier": "online.generalbots",
|
||||
"build": {
|
||||
"beforeDevCommand": "pnpm dev",
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "pnpm build",
|
||||
"frontendDist": "../dist"
|
||||
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"frontendDist": "../out"
|
||||
},
|
||||
"app": {
|
||||
"withGlobalTauri": true,
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
import React from 'react';
|
||||
import '../../styles/ui.css';
|
||||
|
||||
const EMOJI_CATEGORIES = {
|
||||
"😀 🎮": ["😀", "😎", "🤖", "👾", "🎮", "✨", "🚀", "💫"],
|
||||
"🌟 💫": ["⭐", "🌟", "💫", "✨", "⚡", "💥", "🔥", "🌈"],
|
||||
"🤖 🎯": ["🤖", "🎯", "🎲", "🎮", "🕹️", "👾", "💻", "⌨️"]
|
||||
};
|
||||
|
||||
export function EmojiPicker({ visible, onClose, onEmojiSelect }: {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
onEmojiSelect: (emoji: string) => void;
|
||||
}) {
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<div className="emoji-picker-modal">
|
||||
<div className="emoji-picker-header">
|
||||
<h3>Select Emoji</h3>
|
||||
<button onClick={onClose} className="close-button">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M18 6L6 18M6 6l12 12"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="emoji-picker-content">
|
||||
{Object.entries(EMOJI_CATEGORIES).map(([category, emojis]) => (
|
||||
<div key={category} className="emoji-category">
|
||||
<h4 className="category-title">{category}</h4>
|
||||
<div className="emoji-grid">
|
||||
{emojis.map(emoji => (
|
||||
<button
|
||||
key={emoji}
|
||||
className="emoji-button"
|
||||
onClick={() => {
|
||||
onEmojiSelect(emoji);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import React from 'react';
|
||||
import { useSound } from '../../providers/sound-provider';
|
||||
import '../../styles/ui.css';
|
||||
|
||||
export function SettingsPanel() {
|
||||
const [theme, setTheme] = React.useState('dark');
|
||||
const [sound, setSound] = React.useState(true);
|
||||
const [powerMode, setPowerMode] = React.useState(false);
|
||||
const { setEnabled, playSound } = useSound();
|
||||
|
||||
const handleSoundToggle = (value: boolean) => {
|
||||
setSound(value);
|
||||
setEnabled(value);
|
||||
if (value) {
|
||||
playSound('success');
|
||||
}
|
||||
};
|
||||
|
||||
const handleThemeChange = () => {
|
||||
playSound('click');
|
||||
setTheme(theme === 'dark' ? 'light' : 'dark');
|
||||
};
|
||||
|
||||
const handlePowerMode = (value: boolean) => {
|
||||
playSound(value ? 'success' : 'click');
|
||||
setPowerMode(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="settings-panel">
|
||||
<div className="settings-option">
|
||||
{sound ? (
|
||||
<svg className="icon" viewBox="0 0 24 24">
|
||||
<path d="M3 15a1 1 0 011-1h2.83a1 1 0 00.8-.4l1.12-1.5a1 1 0 01.8-.4h3.55a1 1 0 00.8-.4l1.12-1.5a1 1 0 01.8-.4H19a1 1 0 011 1v6a1 1 0 01-1 1h-2.83a1 1 0 00-.8-.4l-1.12-1.5a1 1 0 00-.8-.4H9.72a1 1 0 01-.8-.4l-1.12-1.5a1 1 0 00-.8-.4H4a1 1 0 01-1-1v-2zM12 6a1 1 0 011-1h6a1 1 0 011 1v3"/>
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="icon" viewBox="0 0 24 24">
|
||||
<path d="M3 15a1 1 0 011-1h2.83a1 1 0 00.8-.4l1.12-1.5a1 1 0 01.8-.4h3.55a1 1 0 00.8-.4l1.12-1.5a1 1 0 01.8-.4H19a1 1 0 011 1v6a1 1 0 01-1 1h-2.83a1 1 0 00-.8-.4l-1.12-1.5a1 1 0 00-.8-.4H9.72a1 1 0 01-.8-.4l-1.12-1.5a1 1 0 00-.8-.4H4a1 1 0 01-1-1v-2zM12 6a1 1 0 011-1h6a1 1 0 011 1v3M3 3l18 18"/>
|
||||
</svg>
|
||||
)}
|
||||
<span className="option-text">Sound Effects</span>
|
||||
<label className="switch">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={sound}
|
||||
onChange={(e) => handleSoundToggle(e.target.checked)}
|
||||
/>
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
import { Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
||||
import AuthenticationScreen from './authentication';
|
||||
import { Chat } from './chat';
|
||||
|
||||
import DashboardPage from './dashboard';
|
||||
import TaskPage from './tasks';
|
||||
import TemplatesPage from './templates';
|
||||
import { DriveScreen } from './drive';
|
||||
import SyncPage from './sync/page';
|
||||
import { Button } from './components/ui/button';
|
||||
import MailPage from './mail';
|
||||
|
||||
const examples = [
|
||||
{ name: "Home", href: "authentication" },
|
||||
{ name: "Dashboard", href: "dashboard" },
|
||||
{ name: "Chat", href: "chat" },
|
||||
{ name: "Mail", href: "mail" },
|
||||
{ name: "Drive", href: "drive" },
|
||||
{ name: "Tasks", href: "tasks" },
|
||||
{ name: "Meet", href: "meet" },
|
||||
{ name: "Templates", href: "templates" },
|
||||
{ name: "Settings", href: "sync" },
|
||||
{ name: "Help", href: "help" },
|
||||
];
|
||||
|
||||
const ExamplesNav = () => {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<div className="examples-nav-container">
|
||||
<div className="examples-nav-inner">
|
||||
{examples.map((example) => (
|
||||
<Button
|
||||
key={example.href}
|
||||
onClick={() => navigate(example.href)}
|
||||
className={`example-button ${location.pathname.includes(example.href) ? 'active' : ''
|
||||
}`}
|
||||
>
|
||||
{example.name}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export function RootLayout() {
|
||||
return (
|
||||
<div className="app-container">
|
||||
<ExamplesNav />
|
||||
<main className="app-main">
|
||||
<Routes>
|
||||
<Route path="authentication" element={
|
||||
<>
|
||||
<AuthenticationScreen />
|
||||
</>
|
||||
} />
|
||||
<Route path="chat" element={
|
||||
<>
|
||||
<Chat />
|
||||
|
||||
</>
|
||||
} />
|
||||
<Route path="dashboard" element={
|
||||
<>
|
||||
<DashboardPage />
|
||||
|
||||
</>
|
||||
} />
|
||||
<Route path="mail" element={
|
||||
<>
|
||||
<MailPage />
|
||||
|
||||
</>
|
||||
} />
|
||||
<Route path="drive" element={<DriveScreen />} />
|
||||
<Route path="tasks" element={
|
||||
<>
|
||||
<TaskPage />
|
||||
|
||||
</>
|
||||
} />
|
||||
<Route path="meet" element={<div>Meet Screen (Placeholder)</div>} />
|
||||
<Route path="templates" element={
|
||||
<>
|
||||
<TemplatesPage />
|
||||
|
||||
</>
|
||||
} />
|
||||
<Route path="sync" element={<SyncPage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default RootLayout;
|
25
src/lib/client-cookies.ts
Normal file
25
src/lib/client-cookies.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
'use client';
|
||||
|
||||
export function getClientCookie(key: string): string | null {
|
||||
if (typeof document === 'undefined') return null;
|
||||
|
||||
const value = document.cookie
|
||||
.split('; ')
|
||||
.find(row => row.startsWith(`${key}=`))
|
||||
?.split('=')[1];
|
||||
|
||||
return value ? decodeURIComponent(value) : null;
|
||||
}
|
||||
|
||||
export function setClientCookie(
|
||||
key: string,
|
||||
value: string,
|
||||
options: { expires?: Date; path?: string } = {}
|
||||
): void {
|
||||
if (typeof document === 'undefined') return;
|
||||
|
||||
let cookie = `${key}=${encodeURIComponent(value)}`;
|
||||
if (options.expires) cookie += `; expires=${options.expires.toUTCString()}`;
|
||||
cookie += `; path=${options.path || '/'}`;
|
||||
document.cookie = cookie;
|
||||
}
|
11
src/lib/server-cookies.tsx
Normal file
11
src/lib/server-cookies.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
'use server';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export async function getServerCookie(key: string): Promise<string | undefined> {
|
||||
// This will only run during SSR, not during static export
|
||||
if (process.env.NEXT_PHASE === 'phase-production-build') {
|
||||
return undefined;
|
||||
}
|
||||
return cookies().get(key)?.value;
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
import { Mail } from "@/mail/components/mail";
|
||||
import { accounts, mails } from "@/mail/data";
|
||||
|
||||
export default function MailPage() {
|
||||
const [defaultLayout, setDefaultLayout] = useState(undefined);
|
||||
const [defaultCollapsed, setDefaultCollapsed] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
// In Vite/React, we'll use localStorage instead of cookies
|
||||
const layout = localStorage.getItem("react-resizable-panels:layout:mail");
|
||||
const collapsed = localStorage.getItem("react-resizable-panels:collapsed:mail");
|
||||
|
||||
if (layout) setDefaultLayout(JSON.parse(layout));
|
||||
if (collapsed) setDefaultCollapsed(JSON.parse(collapsed));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="md:hidden">
|
||||
<img
|
||||
src="/examples/mail-dark.png"
|
||||
width={1280}
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="dark:block"
|
||||
/>
|
||||
<img
|
||||
src="/examples/mail-light.png"
|
||||
width={1280}
|
||||
height={727}
|
||||
alt="Mail"
|
||||
className="block dark:hidden"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-col md:flex">
|
||||
<Mail
|
||||
accounts={accounts}
|
||||
mails={mails}
|
||||
defaultLayout={defaultLayout}
|
||||
defaultCollapsed={defaultCollapsed}
|
||||
navCollapsedSize={4}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
13
src/main.tsx
13
src/main.tsx
|
@ -1,13 +0,0 @@
|
|||
import React from 'react'
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import RootLayout from ".";
|
||||
import './styles/globals.css' // or your CSS file path
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(
|
||||
<React.StrictMode>
|
||||
<BrowserRouter>
|
||||
<RootLayout />
|
||||
</BrowserRouter>
|
||||
</React.StrictMode>
|
||||
)
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
/// <reference types="vite/client" />
|
|
@ -2,7 +2,9 @@
|
|||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
},
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
|
@ -19,19 +21,33 @@
|
|||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "preserve",
|
||||
/* Linting */
|
||||
"strict": false,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowJs": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
],
|
||||
"src",
|
||||
|
||||
".next/types/**/*.ts"
|
||||
, "app" ],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
}
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"src-rust"
|
||||
]
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import path from "path";
|
||||
import tailwindcss from "@tailwindcss/vite"
|
||||
|
||||
// @ts-expect-error process is a nodejs global
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
|
||||
plugins: [react(),tailwindcss()],
|
||||
css: {
|
||||
postcss: {
|
||||
config: false // Disable auto-loading of postcss.config.js
|
||||
} as any
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, "./src"),
|
||||
},
|
||||
},
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-rust`
|
||||
ignored: ["**/src-rust/**"],
|
||||
},
|
||||
},
|
||||
}));
|
Loading…
Add table
Reference in a new issue