Skip to content

Commit 5a66d6d

Browse files
committedJan 3, 2025·
Set CTA.tsx UI/Functionalities
1 parent bfcc893 commit 5a66d6d

22 files changed

+1502
-169
lines changed
 

‎package-lock.json

+828-134
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,14 @@
2727
"konva": "^9.3.18",
2828
"lucide-react": "^0.468.0",
2929
"react": "^19.0.0",
30+
"react-confetti": "^6.2.2",
3031
"react-dom": "^19.0.0",
3132
"react-icons": "^5.4.0",
3233
"react-konva": "^19.0.1",
3334
"react-router-dom": "^7.1.1",
3435
"tailwind-merge": "^2.5.5",
3536
"tailwindcss-animate": "^1.0.7",
37+
"tailwindcss-textshadow": "^2.1.3",
3638
"uid": "^2.0.2",
3739
"zustand": "^5.0.2"
3840
},

‎src/App.tsx

+42-24
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,61 @@
1-
import { useState, useEffect, useRef } from 'react';
1+
import { useEffect, useRef } from 'react';
22
import { BrowserRouter, Routes, Route } from 'react-router-dom';
33

44
// SERVICES
55
import { checkSession } from './backend/services/auth/checkSession';
66

77
// STORES
88
import useIsUserLoggedInState from './stores/backend/useIsLoggedinState';
9+
import { useLoadingScreen } from './stores';
910

1011
// PAGES
11-
import { Dashboard } from './pages';
12-
import { Auth } from './pages';
12+
import { CTA, Dashboard } from './pages';
13+
import { Auth } from './pages';
1314

1415
// UI
1516
import { LoadingState } from './components';
1617
import { Toaster } from "@/components/ui/toaster"
1718

1819

1920
export default function App() {
21+
22+
23+
// check and set if user is logged in
2024
const { isUserLoggedIn, setIsUserLoggedIn } = useIsUserLoggedInState();
21-
const [sessionChecked, setSessionChecked] = useState(false);
25+
26+
// Loading screen
27+
const { loadingScreen, setLoadingScreen } = useLoadingScreen();
2228

2329
// Reference to the audio element
2430
const audioRef = useRef<HTMLAudioElement>(null);
2531

26-
// Check if there's an active session by calling the checkSession() and check it's returns
27-
async function sessionCheck() {
28-
try {
29-
const response = await checkSession();
30-
setIsUserLoggedIn(response);
31-
} catch (error) {
32-
console.error('Error checking session:', error);
33-
setIsUserLoggedIn(false);
34-
} finally {
35-
setSessionChecked(true);
36-
}
37-
}
38-
39-
useEffect(() => {
40-
sessionCheck()
41-
}, []);
42-
4332
// Play audio when user logs in
4433
useEffect(() => {
4534
if (isUserLoggedIn && audioRef.current) {
4635
audioRef.current.play();
4736
}
4837
}, [isUserLoggedIn]);
4938

39+
useEffect(() => {
40+
const initializeSession = async () => {
41+
setLoadingScreen(true);
42+
try {
43+
const res = await checkSession();
44+
setIsUserLoggedIn(res === true && true);
45+
} catch (error) {
46+
console.error("Error checking session:", error);
47+
setIsUserLoggedIn(false);
48+
} finally {
49+
setLoadingScreen(false);
50+
}
51+
};
52+
53+
initializeSession();
54+
}, []);
55+
56+
5057
// loading indicator
51-
if (!sessionChecked) {
58+
if (loadingScreen) {
5259
return (
5360
<div className="flex justify-center items-center w-full h-screen">
5461
<LoadingState setWidth="50" />
@@ -59,10 +66,21 @@ export default function App() {
5966

6067
return (
6168
<BrowserRouter>
62-
<Toaster />
69+
<Toaster />
6370

6471
<Routes>
65-
<Route index path="/" element={isUserLoggedIn ? <Dashboard /> : <Auth />} />
72+
{isUserLoggedIn ? (
73+
<>
74+
<Route index path="/" element={<CTA />} />
75+
<Route path="/:fileId" element={<Dashboard />} />
76+
</>
77+
) : (
78+
<>
79+
<Route index path="/" element={<Auth />} />
80+
<Route path="*" element={<Auth />} />
81+
</>
82+
)}
83+
6684
</Routes>
6785

6886
{/* Login Sound Effect */}

‎src/backend/services/auth/checkSession.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { account } from "@/backend/configs/configs"
33

44

55
export async function checkSession() {
6-
const results = await account.getSession('current')
7-
return results ? true : false
6+
const res = await account.getSession('current')
7+
.then((res) => {
8+
return res && true;
9+
}).catch((err) => {
10+
return err && false;
11+
})
12+
13+
return res;
814
}

‎src/backend/services/auth/userData.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { account } from "@/backend/configs/configs"
2+
import { setUserData } from "@/stores/backend/useUserDataStore";
3+
4+
5+
export async function userData() {
6+
const res = await account.get()
7+
.then((res) => {
8+
setUserData(res);
9+
return true;
10+
}).catch(() => {
11+
return false;
12+
})
13+
14+
return res;
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { databases } from "@/backend/configs/configs";
2+
import { searchData_interface } from "@/interfaces";
3+
import { setAllFiles } from "@/stores/backend/useAllFiles";
4+
import { Query } from "appwrite";
5+
6+
7+
export async function fetchAllFiles(searchData: searchData_interface) {
8+
9+
const queries = [
10+
Query.orderDesc('$createdAt'),
11+
...(searchData.searchBy !== "" && searchData.searchValue !== "" ? [Query.equal(`${searchData.searchBy}`, [`${searchData.searchValue}`])] : [])
12+
];
13+
14+
try {
15+
const results = await databases.listDocuments(
16+
`${import.meta.env.VITE_DATABASES_MAIN}`,
17+
`${import.meta.env.VITE_COLL_DOCS}`,
18+
queries
19+
);
20+
setAllFiles(results.documents);
21+
return true;
22+
} catch (err) {
23+
console.error('Error fetching files:', err);
24+
return false;
25+
}
26+
}

‎src/components/common/left-section/Menu.tsx

+8-4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { Link } from "react-router-dom";
2+
13
// UI
24
import {
35
Menubar,
@@ -32,7 +34,7 @@ export default function Menu() {
3234
if (res) {
3335
window.location.reload()
3436
} else {
35-
console.log('Can not logout! something went wrong while logging out!');
37+
console.log('Can not logout! something went wrong while logging out!, Please reload the page and try again.');
3638
}
3739
}
3840

@@ -49,9 +51,11 @@ export default function Menu() {
4951
<MenubarItem className="cursor-pointer hover:font-semibold">
5052
Create new <MenubarShortcut><FcFile className="size-4" /></MenubarShortcut>
5153
</MenubarItem>
52-
<MenubarItem className="cursor-pointer hover:font-semibold">
53-
open file <MenubarShortcut><FcOpenedFolder className="size-4" /></MenubarShortcut>
54-
</MenubarItem>
54+
<Link to="/">
55+
<MenubarItem className="cursor-pointer hover:font-semibold">
56+
open file <MenubarShortcut><FcOpenedFolder className="size-4" /></MenubarShortcut>
57+
</MenubarItem>
58+
</Link>
5559
<MenubarSeparator className="flex md:hidden" />
5660
<MenubarItem className="flex md:hidden cursor-pointer hover:font-semibold">
5761
Sync updates <MenubarShortcut><FcUpload className="size-4" /></MenubarShortcut>
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useState, useEffect } from "react";
2+
3+
// STORES
4+
import useUserDataStore from "@/stores/backend/useUserDataStore";
5+
6+
export function useGreeting() {
7+
8+
// Is First-Time-User State
9+
const [isFTU, setIsFTU] = useState<boolean>(false);
10+
11+
// Greeting Message State
12+
const [greeting, setGreeting] = useState<{
13+
isFTU: boolean;
14+
username: string;
15+
greetingMsg: string
16+
}>({
17+
isFTU: isFTU,
18+
username: "",
19+
greetingMsg: "",
20+
});
21+
22+
// Get User Data from the store
23+
const userData = useUserDataStore((state) => state.userData);
24+
25+
useEffect(() => {
26+
if (userData) {
27+
// Check if user is a First-time user
28+
const registrationTime = new Date(userData.registration);
29+
const accessedTime = new Date(userData.accessedAt);
30+
31+
// Determine if First-Time-User
32+
setIsFTU(registrationTime.getTime() === accessedTime.getTime());
33+
34+
if (isFTU) {
35+
setGreeting({
36+
isFTU: true,
37+
username: `Welcome ${userData.name}! 🎉`,
38+
greetingMsg: `Excited to have you here! 🌟 Ready to unleash your creativity? Start a new workspace and let your ideas flow. Let's make something amazing together! 🚀`,
39+
});
40+
} else {
41+
setGreeting({
42+
isFTU: false,
43+
username: `Welcome back, ${userData.name}! 👋`,
44+
greetingMsg: `Great to see you again! 🌟 What's sparking your creativity today? Dive into a new workspace or breathe new life into an existing one. Let's continue making amazing things together! 🚀`,
45+
});
46+
}
47+
}
48+
}, [userData, isFTU]);
49+
50+
return greeting;
51+
}
52+
53+
export function CTA_GreetingMsg() {
54+
const greeting = useGreeting();
55+
return {
56+
isFTU: greeting.isFTU,
57+
username: greeting.username,
58+
greetingMsg: greeting.greetingMsg
59+
}
60+
}

‎src/components/cta/CTA_Header.tsx

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { useState, useEffect } from "react";
2+
import Confetti from "react-confetti";
3+
4+
// STORES
5+
import useUserDataStore from "@/stores/backend/useUserDataStore";
6+
import { useLoadingScreen } from "@/stores";
7+
8+
// UI
9+
import { Button } from "../ui/button";
10+
11+
// ICONS
12+
import { BiLogOut } from "react-icons/bi";
13+
14+
// SERVICES
15+
import { logout } from "@/backend/services/auth/logout";
16+
17+
export default function CTA_Header() {
18+
19+
// Loading screen
20+
const { setLoadingScreen } = useLoadingScreen();
21+
22+
// get user data from the store
23+
const { userData } = useUserDataStore();
24+
25+
// Is First-Time-User State
26+
const [isFTU, setIsFTU] = useState<boolean>(false);
27+
28+
// Greeting Message State
29+
const [greeting, setGreeting] = useState<{
30+
isFTU: boolean;
31+
username: string;
32+
greetingMsg: string;
33+
}>({
34+
isFTU: false,
35+
username: "",
36+
greetingMsg: "",
37+
});
38+
39+
40+
function initialize() {
41+
setLoadingScreen(true);
42+
if (userData) {
43+
44+
// Check if user is a First-time user
45+
const registrationTime = new Date(userData.registration);
46+
const currentTime = new Date();
47+
48+
console.log('userData.registration: ', userData.registration);
49+
console.log('Current time: ', currentTime);
50+
51+
// Determine if First-Time-User based on a threshold (e.g., 1 hour)
52+
const thresholdInMilliseconds = 60 * 60 * 1000; // 1 hour
53+
const isFirstTimeUser = currentTime.getTime() - registrationTime.getTime() < thresholdInMilliseconds;
54+
setIsFTU(isFirstTimeUser);
55+
56+
if (isFirstTimeUser) {
57+
// Change the page title to username
58+
document.title = `Welcome ${userData.name}! 🎉`;
59+
60+
setGreeting({
61+
isFTU: true,
62+
username: `Welcome ${userData.name}! 🎉`,
63+
greetingMsg: `Excited to have you here!✨ Ready to unleash your creativity? Whether starting fresh with a new workspace or diving into an existing file, let your ideas come to life and create something truly amazing! 🚀`,
64+
});
65+
} else {
66+
// Change the page title to username
67+
document.title = `Welcome back, ${userData.name}! 👋`;
68+
69+
setGreeting({
70+
isFTU: false,
71+
username: `Welcome back, ${userData.name}! 👋`,
72+
greetingMsg: `Great to see you again! 🌟 What's sparking your creativity today? Dive into a new workspace or breathe new life into an existing one. Let's continue making amazing things together! 🚀`,
73+
});
74+
}
75+
}
76+
setLoadingScreen(false);
77+
};
78+
79+
useEffect(() => {
80+
initialize();
81+
}, [userData]);
82+
83+
// handle Logout func.
84+
async function handleLogout() {
85+
const res = await logout();
86+
if (res) {
87+
window.location.reload()
88+
} else {
89+
console.log('Can not logout! something went wrong while logging out!');
90+
}
91+
}
92+
93+
94+
return (
95+
<>
96+
{/* Confetti effect for new users */}
97+
{isFTU && (
98+
<Confetti
99+
run={true}
100+
recycle={false}
101+
numberOfPieces={1000}
102+
width={window.innerWidth}
103+
height={window.innerHeight}
104+
/>
105+
)}
106+
107+
{/* Welcome Note */}
108+
<div className="text-center w-full my-6 space-y-8">
109+
<div className="fixed text-left m-0">
110+
<Button onClick={handleLogout} variant={"destructive"} className="rounded-lg shadow-md font-semibold flex items-center space-x-2">
111+
<BiLogOut className="size-4" />
112+
Logout
113+
</Button>
114+
</div>
115+
<h1 className="text-5xl font-extrabold capitalize text-shadow-md">{greeting.username}</h1>
116+
<p className="w-[800px] text-gray-500 font-semibold mx-auto">{greeting.greetingMsg}</p>
117+
</div>
118+
</>
119+
);
120+
}

‎src/components/cta/CTA_Main.tsx

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import { useEffect, useState } from "react"
2+
3+
// UI
4+
import {
5+
Table,
6+
TableBody,
7+
TableCell,
8+
TableHead,
9+
TableHeader,
10+
TableRow,
11+
} from "@/components/ui/table"
12+
import { ScrollArea } from "@/components/ui/scroll-area"
13+
import { Separator } from "../ui/separator"
14+
import { Button } from "@/components/ui/button"
15+
import LoadingState from "../ui/LoadingState"
16+
import {
17+
Select,
18+
SelectContent,
19+
SelectItem,
20+
SelectTrigger,
21+
SelectValue,
22+
} from "@/components/ui/select"
23+
24+
25+
// STORES
26+
import useAllFiles from "@/stores/backend/useAllFiles"
27+
28+
// INTERFACES
29+
import { fetchedWorkspaces_interface } from "@/interfaces"
30+
31+
// SERVICES
32+
import { fetchAllFiles } from "@/backend/services/files/fetchAllFiles"
33+
34+
// ICONS
35+
import { FaRegFolderOpen } from "react-icons/fa";
36+
import { Link } from "react-router-dom"
37+
38+
39+
40+
export default function CTA_Main() {
41+
42+
43+
// Loading screen
44+
const [loadingFiles, setLoadingFiles] = useState<boolean>(true);
45+
46+
// Collect Search Data
47+
const [searchData, setSearchData] = useState<{
48+
searchBy: string,
49+
searchValue: string,
50+
}>({
51+
searchBy: "fileId",
52+
searchValue: "",
53+
});
54+
55+
// Get all files
56+
const { allFiles } = useAllFiles();
57+
58+
async function initialize() {
59+
const getFiles = await fetchAllFiles(searchData);
60+
if (getFiles) {
61+
setLoadingFiles(false);
62+
} else {
63+
setLoadingFiles(false);
64+
}
65+
}
66+
67+
const handleSearch = () => {
68+
setLoadingFiles(true);
69+
initialize();
70+
};
71+
72+
useEffect(() => {
73+
initialize();
74+
}, []);
75+
76+
return (
77+
<div className="flex-col space-y-4 max-w-[800px] mx-auto">
78+
<Separator className="w-[500px] mx-auto" />
79+
80+
{/* START NEW WORKSPACE */}
81+
<div className="flex justify-between space-x-2">
82+
83+
{/* Search By */}
84+
<Select defaultValue="fileId" onValueChange={(value) => setSearchData({ ...searchData, searchBy: value })}>
85+
<SelectTrigger className="max-w-[200px] h-[40px] bg-white border-[1px] shadow-sm border-gray-200 rounded-md font-semibold">
86+
<SelectValue placeholder="Search By" />
87+
</SelectTrigger>
88+
<SelectContent>
89+
<SelectItem value="fileId">File ID <span className="text-gray-700">(recommended)</span></SelectItem>
90+
<SelectItem value="fileName">File Title</SelectItem>
91+
<SelectItem value="fileCreator">File Creator</SelectItem>
92+
</SelectContent>
93+
</Select>
94+
95+
96+
{/* Search Bar */}
97+
<input
98+
type="text"
99+
placeholder={`Search By ${searchData.searchBy === "fileId" ? "file ID" : searchData.searchBy === "fileName" ? "file title" : searchData.searchBy === "fileCreator" ? "Creator name" : 'file ID'}`.toLowerCase()}
100+
className="w-full h-[40px] px-4 border-[1px] shadow-sm border-gray-200 rounded-md capitalize"
101+
onChange={(e) => setSearchData({ ...searchData, searchValue: e.target.value })}
102+
/>
103+
104+
<Button onClick={handleSearch} className="h-[40px] shadow-sm">Search</Button>
105+
106+
{/* Vertical line for space */}
107+
<div className="w-[2px] h-[40px] bg-gray-200"></div>
108+
109+
{/* Start New Workspace Btn */}
110+
<Button
111+
className="w-[200px] h-[40px] shadow-md bg-blue-500 hover:bg-blue-600 active:bg-blue-700 font-semibold text-center"
112+
> Start New Workspace ✨
113+
</Button>
114+
</div>
115+
116+
{/* WORKSPACES Table */}
117+
<div className="border-[1px] border-gray-200 rounded-md overflow-hidden p-1 bg-white">
118+
119+
{loadingFiles ? (
120+
<div className="flex justify-center items-center w-full h-[270px]">
121+
<LoadingState setWidth="50" />
122+
<p className="text-gray-500 font-semibold">Loading files..</p>
123+
</div>
124+
) : (
125+
<>
126+
<Table>
127+
<TableHeader className="sticky top-0 bg-white z-10 uppercase font-semibold">
128+
<TableRow>
129+
<TableHead className="w-[95px]">File ID</TableHead>
130+
<TableHead className="w-[100px]">Title</TableHead>
131+
<TableHead className="w-[100px]">Creator</TableHead>
132+
<TableHead className="w-[100px]">Latest Editor</TableHead>
133+
<TableHead className="w-[100px]">Last Update</TableHead>
134+
<TableHead className="w-[100px] text-right"></TableHead>
135+
</TableRow>
136+
</TableHeader>
137+
</Table>
138+
<ScrollArea className="h-[250px] bg-white">
139+
<Table>
140+
<TableBody>
141+
{allFiles?.length > 0 ? allFiles?.map((workspace: fetchedWorkspaces_interface) => (
142+
<TableRow key={workspace.id}>
143+
<TableCell className="w-[95px] max-w-[95px] font-medium text-ellipsis overflow-hidden whitespace-nowrap">{workspace.fileId}</TableCell>
144+
<TableCell className="w-[100px] max-w-[100px]">{workspace.fileName}</TableCell>
145+
<TableCell className="w-[100px] max-w-[100px] text-ellipsis overflow-hidden whitespace-nowrap">{workspace.fileCreator}</TableCell>
146+
<TableCell className="w-[100px] max-w-[100px] text-ellipsis overflow-hidden whitespace-nowrap">{workspace.fileLatestEditor === "" ? workspace.fileCreator : workspace.fileLatestEditor}</TableCell>
147+
<TableCell className="w-[100px] max-w-[100px] text-ellipsis overflow-hidden whitespace-nowrap">
148+
{new Date(workspace.$updatedAt).toISOString().split('T')[0]}
149+
</TableCell>
150+
<TableCell className="w-[100px] max-w-[100px] text-right">
151+
<Link to={`/${workspace.fileId}`}>
152+
<Button variant="default" size="sm" className="w-full flex justify-between items-center">
153+
Open file
154+
<FaRegFolderOpen className="!size-4" />
155+
</Button>
156+
</Link>
157+
</TableCell>
158+
</TableRow>
159+
)) : (
160+
<div className="flex justify-center items-center w-full h-[250px]">
161+
<span className="text-center mx-auto text-gray-500 font-semibold">No files found</span>
162+
</div>
163+
)}
164+
</TableBody>
165+
</Table>
166+
</ScrollArea>
167+
</>
168+
)}
169+
170+
</div>
171+
</div>
172+
)
173+
}
174+

‎src/components/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export { default as Preview } from './preview/Preview';
55
export { default as Displays } from './displays/Displays';
66
export { default as LoadingState } from './ui/LoadingState';
77
export { default as AuthContent } from './auth/AuthContent';
8+
export { default as CTA_Header } from './cta/CTA_Header';
9+
export { default as CTA_Main } from './cta/CTA_Main';
810

‎src/components/ui/table.tsx

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import * as React from "react"
2+
3+
import { cn } from "@/lib/utils"
4+
5+
const Table = React.forwardRef<
6+
HTMLTableElement,
7+
React.HTMLAttributes<HTMLTableElement>
8+
>(({ className, ...props }, ref) => (
9+
<div className="relative w-full overflow-auto">
10+
<table
11+
ref={ref}
12+
className={cn("w-full caption-bottom text-sm", className)}
13+
{...props}
14+
/>
15+
</div>
16+
))
17+
Table.displayName = "Table"
18+
19+
const TableHeader = React.forwardRef<
20+
HTMLTableSectionElement,
21+
React.HTMLAttributes<HTMLTableSectionElement>
22+
>(({ className, ...props }, ref) => (
23+
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
24+
))
25+
TableHeader.displayName = "TableHeader"
26+
27+
const TableBody = React.forwardRef<
28+
HTMLTableSectionElement,
29+
React.HTMLAttributes<HTMLTableSectionElement>
30+
>(({ className, ...props }, ref) => (
31+
<tbody
32+
ref={ref}
33+
className={cn("[&_tr:last-child]:border-0", className)}
34+
{...props}
35+
/>
36+
))
37+
TableBody.displayName = "TableBody"
38+
39+
const TableFooter = React.forwardRef<
40+
HTMLTableSectionElement,
41+
React.HTMLAttributes<HTMLTableSectionElement>
42+
>(({ className, ...props }, ref) => (
43+
<tfoot
44+
ref={ref}
45+
className={cn(
46+
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0",
47+
className
48+
)}
49+
{...props}
50+
/>
51+
))
52+
TableFooter.displayName = "TableFooter"
53+
54+
const TableRow = React.forwardRef<
55+
HTMLTableRowElement,
56+
React.HTMLAttributes<HTMLTableRowElement>
57+
>(({ className, ...props }, ref) => (
58+
<tr
59+
ref={ref}
60+
className={cn(
61+
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
62+
className
63+
)}
64+
{...props}
65+
/>
66+
))
67+
TableRow.displayName = "TableRow"
68+
69+
const TableHead = React.forwardRef<
70+
HTMLTableCellElement,
71+
React.ThHTMLAttributes<HTMLTableCellElement>
72+
>(({ className, ...props }, ref) => (
73+
<th
74+
ref={ref}
75+
className={cn(
76+
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
77+
className
78+
)}
79+
{...props}
80+
/>
81+
))
82+
TableHead.displayName = "TableHead"
83+
84+
const TableCell = React.forwardRef<
85+
HTMLTableCellElement,
86+
React.TdHTMLAttributes<HTMLTableCellElement>
87+
>(({ className, ...props }, ref) => (
88+
<td
89+
ref={ref}
90+
className={cn(
91+
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
92+
className
93+
)}
94+
{...props}
95+
/>
96+
))
97+
TableCell.displayName = "TableCell"
98+
99+
const TableCaption = React.forwardRef<
100+
HTMLTableCaptionElement,
101+
React.HTMLAttributes<HTMLTableCaptionElement>
102+
>(({ className, ...props }, ref) => (
103+
<caption
104+
ref={ref}
105+
className={cn("mt-4 text-sm text-muted-foreground", className)}
106+
{...props}
107+
/>
108+
))
109+
TableCaption.displayName = "TableCaption"
110+
111+
export {
112+
Table,
113+
TableHeader,
114+
TableBody,
115+
TableFooter,
116+
TableHead,
117+
TableRow,
118+
TableCell,
119+
TableCaption,
120+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default interface searchData_interface {
2+
searchBy: string;
3+
searchValue: string;
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export default interface fetchedWorkspaces_interface {
2+
id: string;
3+
fileId: string;
4+
fileName: string;
5+
fileCreator: string;
6+
fileLatestEditor: string;
7+
$updatedAt: string;
8+
}
9+

‎src/interfaces/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@ export type { default as handleImgUpload_interface } from './helpers/uploadingMe
55
export type { default as handleVideoUpload_interface } from './helpers/uploadingMedia/handleVideoUpload_interface'
66
export type { default as useIsSaving_interface } from './footer/useIsSaving_interface'
77
export type { default as useIsUserLoggedInState_interface } from './backend/useIsUserLoggedInState_interface'
8+
export type { default as useLoadingScreen_interface } from './ui/useLoadingScreen_interface'
9+
export type { default as fetchedWorkspaces_interface } from './cta/fetchedWorkspaces_interface'
10+
export type { default as searchData_interface } from './backend/searchData_interface'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export default interface useLoadingScreen_interface {
2+
loadingScreen: boolean;
3+
setLoadingScreen: (state: boolean) => void;
4+
}

‎src/pages/CTA.tsx

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
1-
export default function CAT() {
1+
import { useEffect } from "react";
2+
3+
// UI
4+
import { CTA_Header, CTA_Main } from "@/components";
5+
6+
// SERVICES
7+
import { userData } from "@/backend/services/auth/userData";
8+
9+
export default function CTA() {
10+
11+
// get user data from the store
12+
useEffect(() => {
13+
userData();
14+
}, []);
15+
216
return (
3-
<>
4-
CTA
5-
</>
17+
<div className="py-6 px-16">
18+
<CTA_Header />
19+
<CTA_Main />
20+
</div>
621
)
722
}

‎src/stores/backend/useAllFiles.ts

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { create } from 'zustand';
2+
3+
interface allFiles_Object_interface {
4+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5+
[key: string]: any;
6+
}
7+
8+
interface useAllFiles_interface {
9+
allFiles: allFiles_Object_interface | null;
10+
setAllFiles: (state: allFiles_Object_interface) => void;
11+
}
12+
13+
export const useAllFiles = create<useAllFiles_interface>((set) => ({
14+
allFiles: null, // Initialize as null
15+
setAllFiles: (state) => set({ allFiles: state }),
16+
}));
17+
18+
export const getAllFiles = useAllFiles.getState().allFiles;
19+
export const setAllFiles = useAllFiles.getState().setAllFiles;
20+
export default useAllFiles;
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { create } from 'zustand';
2+
3+
interface UserData {
4+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5+
[key: string]: any;
6+
}
7+
8+
interface useUserDataStore_interface {
9+
userData: UserData | null;
10+
setUserData: (state: UserData) => void;
11+
}
12+
13+
export const useUserDataStore = create<useUserDataStore_interface>((set) => ({
14+
userData: null, // Initialize as null
15+
setUserData: (state) => set({ userData: state }),
16+
}));
17+
18+
export const getUserData = useUserDataStore.getState().userData;
19+
export const setUserData = useUserDataStore.getState().setUserData;
20+
export default useUserDataStore;

‎src/stores/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { default as useTools } from './tools/useTools'
22
export { default as useFileName } from './settings/useFileName'
33
export { default as useCanvasStore } from './canvasStore/useCanvasStore'
4+
export { default as useLoadingScreen } from './ui/useLoadingScreen'

‎src/stores/ui/useLoadingScreen.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { create } from 'zustand';
2+
import { useLoadingScreen_interface } from '@/interfaces';
3+
4+
export const useLoadingScreen = create<useLoadingScreen_interface>((set) => ({
5+
loadingScreen: false,
6+
setLoadingScreen: (state) => set({ loadingScreen: state }),
7+
}));
8+
9+
export default useLoadingScreen;

‎tailwind.config.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/** @type {import('tailwindcss').Config} */
22
import tailwindcssAnimate from 'tailwindcss-animate';
3+
import tailwindcssTextShadow from 'tailwindcss-textshadow';
34

45
export default {
56
darkMode: ["class"],
@@ -9,6 +10,12 @@ export default {
910
],
1011
theme: {
1112
extend: {
13+
textShadow: {
14+
'default': '0 2px 4px rgba(0, 0, 0, 0.10)',
15+
'md': '0 4px 6px rgba(0, 0, 0, 0.10)',
16+
'lg': '0 10px 15px rgba(0, 0, 0, 0.10)',
17+
'xl': '0 20px 25px rgba(0, 0, 0, 0.10)',
18+
},
1219
borderRadius: {
1320
lg: 'var(--radius)',
1421
md: 'calc(var(--radius) - 2px)',
@@ -58,5 +65,5 @@ export default {
5865
}
5966
}
6067
},
61-
plugins: [tailwindcssAnimate],
68+
plugins: [tailwindcssAnimate, tailwindcssTextShadow],
6269
};

0 commit comments

Comments
 (0)
Please sign in to comment.