├── src ├── app │ ├── (dashboard) │ │ ├── dashboard │ │ │ ├── main.module.css │ │ │ ├── settings │ │ │ │ └── page.tsx │ │ │ ├── shop │ │ │ │ └── page.tsx │ │ │ └── page.tsx │ │ ├── styles │ │ │ └── main.module.css │ │ ├── _components │ │ │ ├── NewsScroll │ │ │ │ ├── NewsScroll.module.css │ │ │ │ └── NewsScroll.tsx │ │ │ ├── Header │ │ │ │ ├── Header.module.css │ │ │ │ └── Header.tsx │ │ │ ├── FeatureMain │ │ │ │ ├── FeaturesSection │ │ │ │ │ ├── Feature │ │ │ │ │ │ ├── Features │ │ │ │ │ │ │ └── TodoList │ │ │ │ │ │ │ │ ├── TodoList.module.css │ │ │ │ │ │ │ │ ├── CreateTodoForm │ │ │ │ │ │ │ │ ├── CreateTodo.module.css │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── Task │ │ │ │ │ │ │ │ ├── Task.module.css │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ ├── UpdateTaskForm │ │ │ │ │ │ │ │ ├── UpdateTaskForm.module.css │ │ │ │ │ │ │ │ └── index.tsx │ │ │ │ │ │ │ │ └── TodoList.tsx │ │ │ │ │ │ └── Feature.tsx │ │ │ │ │ ├── FeaturesSectionStyles.module.css │ │ │ │ │ └── FeaturesSection.tsx │ │ │ │ ├── FeatureMain.module.css │ │ │ │ └── FeatureMain.tsx │ │ │ ├── FeatureButtonContainer │ │ │ │ ├── FeatureButtonContainer.module.css │ │ │ │ └── FeatureButtonContainer.tsx │ │ │ ├── FeatureButton │ │ │ │ ├── FeatureButton.module.css │ │ │ │ └── FeatureButton.tsx │ │ │ └── Sidebar │ │ │ │ ├── SideBarStyles.module.css │ │ │ │ └── Sidebar.tsx │ │ └── layout.tsx │ ├── (main) │ │ ├── styles │ │ │ ├── layoutstyles.module.css │ │ │ └── mainStyles.module.css │ │ ├── assets │ │ │ └── hero.png │ │ ├── (auth) │ │ │ ├── error │ │ │ │ └── page.tsx │ │ │ ├── layout.tsx │ │ │ └── auth │ │ │ │ ├── auth.module.css │ │ │ │ ├── page.tsx │ │ │ │ ├── confirm │ │ │ │ └── route.ts │ │ │ │ └── actions │ │ │ │ └── index.ts │ │ ├── _components │ │ │ ├── Footer │ │ │ │ ├── Footer.module.css │ │ │ │ └── Footer.tsx │ │ │ └── Navbar │ │ │ │ ├── Navbar.module.css │ │ │ │ └── Navbar.tsx │ │ ├── (shop) │ │ │ ├── shop │ │ │ │ └── page.tsx │ │ │ └── _components │ │ │ │ ├── componentStyles.module.css │ │ │ │ └── Shop.tsx │ │ ├── layout.tsx │ │ ├── (about) │ │ │ └── about │ │ │ │ ├── About.module.css │ │ │ │ └── page.tsx │ │ ├── (capstone) │ │ │ └── capstone │ │ │ │ ├── capstone.module.css │ │ │ │ └── page.tsx │ │ └── page.tsx │ ├── favicon.ico │ ├── globals.css │ └── layout.tsx ├── lib │ └── types.ts ├── utils │ └── supabase │ │ ├── client.ts │ │ ├── server.ts │ │ └── middleware.ts ├── components │ └── MainProvider.tsx ├── data │ └── data.ts ├── redux │ └── store.ts ├── api │ └── todoApi.ts └── slices │ └── todosSlice.ts ├── .eslintrc.json ├── next.config.mjs ├── .gitignore ├── public ├── vercel.svg └── next.svg ├── tsconfig.json ├── middleware.ts ├── README.md └── package.json /src/app/(dashboard)/dashboard/main.module.css: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(dashboard)/styles/main.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(main)/styles/layoutstyles.module.css: -------------------------------------------------------------------------------- 1 | .homelayout { 2 | width: 100%; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mays88/Capstone/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/lib/types.ts: -------------------------------------------------------------------------------- 1 | export type featureWindowProps = React.Dispatch>; 2 | -------------------------------------------------------------------------------- /src/app/(main)/assets/hero.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mays88/Capstone/HEAD/src/app/(main)/assets/hero.png -------------------------------------------------------------------------------- /next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/error/page.tsx: -------------------------------------------------------------------------------- 1 | export default function ErrorPage() { 2 | return

Sorry, something went wrong

; 3 | } 4 | -------------------------------------------------------------------------------- /src/app/(dashboard)/dashboard/settings/page.tsx: -------------------------------------------------------------------------------- 1 | function SettingsPage() { 2 | return
Settings Page
; 3 | } 4 | export default SettingsPage; 5 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/NewsScroll/NewsScroll.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: #00000093; 3 | width: 100%; 4 | height: 25px; 5 | color: white; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/layout.tsx: -------------------------------------------------------------------------------- 1 | type AuthProps = { 2 | children: React.ReactNode; 3 | }; 4 | export default async function AuthLayout({ children }: AuthProps) { 5 | return <>{children}; 6 | } 7 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/Header/Header.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | width: 100%; 6 | height: 3rem; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/NewsScroll/NewsScroll.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./NewsScroll.module.css"; 2 | 3 | function NewsScroll() { 4 | return
NewsScroll
; 5 | } 6 | export default NewsScroll; 7 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/TodoList.module.css: -------------------------------------------------------------------------------- 1 | .todoWrapper { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | width: 85%; 6 | margin-top: 2em; 7 | } 8 | -------------------------------------------------------------------------------- /src/app/(main)/_components/Footer/Footer.module.css: -------------------------------------------------------------------------------- 1 | .navbarContainer { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | height: 3.5rem; 6 | padding: 0 1em; 7 | } 8 | 9 | .links { 10 | padding: 10px; 11 | } 12 | -------------------------------------------------------------------------------- /src/utils/supabase/client.ts: -------------------------------------------------------------------------------- 1 | import { createBrowserClient } from "@supabase/ssr"; 2 | 3 | export function createClient() { 4 | return createBrowserClient( 5 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 6 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /src/app/(main)/(shop)/shop/page.tsx: -------------------------------------------------------------------------------- 1 | import Shop from "../_components/Shop"; 2 | 3 | export default async function Home() { 4 | // const recipes = await getRecipes(); 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureButtonContainer/FeatureButtonContainer.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 7rem; 3 | height: 7rem; 4 | border: 1px solid black; 5 | border-radius: 15%; 6 | } 7 | 8 | .featureGrid { 9 | display: flex; 10 | gap: 25px; 11 | margin: 100px; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/(dashboard)/dashboard/shop/page.tsx: -------------------------------------------------------------------------------- 1 | import Shop from "../../../(main)/(shop)/_components/Shop"; 2 | 3 | export default async function DashShop() { 4 | // const recipes = await getRecipes(); 5 | return ( 6 |
7 | 8 |
9 | ); 10 | } 11 | -------------------------------------------------------------------------------- /src/components/MainProvider.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { store } from "@/redux/store"; 4 | 5 | import { Provider } from "react-redux"; 6 | 7 | export default function MainProvider({ 8 | children, 9 | }: { 10 | children: React.ReactNode; 11 | }) { 12 | return {children}; 13 | } 14 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureButtonContainer/FeatureButtonContainer.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./FeatureButtonContainer.module.css"; 2 | 3 | function FeatureButtonContainer({ children }: { children: React.ReactNode }) { 4 | return
{children}
; 5 | } 6 | export default FeatureButtonContainer; 7 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/Header/Header.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./Header.module.css"; 2 | function Header() { 3 | return ( 4 |
5 |
6 |
10 Sept
7 |
Calendar
8 |
9 | ); 10 | } 11 | export default Header; 12 | -------------------------------------------------------------------------------- /src/app/globals.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | background-color: #dcc756; 9 | } 10 | a { 11 | text-decoration: none; 12 | } 13 | 14 | a:hover { 15 | color: #646464; 16 | } 17 | a:visited { 18 | color: #000; 19 | } 20 | 21 | img { 22 | max-width: 100%; 23 | display: block; 24 | } 25 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureButton/FeatureButton.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | border-radius: 15%; 3 | display: flex; 4 | justify-content: center; 5 | align-items: center; 6 | height: 7rem; 7 | width: 7rem; 8 | background-color: #d9c147; 9 | border: 2px solid #d1b000; 10 | box-shadow: 0px 3px 5px 1px #636363; 11 | margin: 0.125em; 12 | } 13 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/FeaturesSectionStyles.module.css: -------------------------------------------------------------------------------- 1 | .featureContainer { 2 | display: flex; 3 | height: 100%; 4 | margin: auto; 5 | width: 90%; 6 | } 7 | 8 | .featureSection { 9 | margin: auto; 10 | width: 50%; 11 | height: 100%; 12 | border: 1px solid #000; 13 | display: flex; 14 | justify-content: center; 15 | } 16 | -------------------------------------------------------------------------------- /src/app/(main)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Footer from "./_components/Footer/Footer"; 2 | import Navbar from "./_components/Navbar/Navbar"; 3 | import styles from "./styles/layoutstyles.module.css"; 4 | function layout({ children }: { children: React.ReactNode }) { 5 | return ( 6 |
7 | 8 | {children} 9 |
10 |
11 | ); 12 | } 13 | export default layout; 14 | -------------------------------------------------------------------------------- /src/app/(main)/(shop)/_components/componentStyles.module.css: -------------------------------------------------------------------------------- 1 | .itemContainer { 2 | display: flex; 3 | padding: 3em; 4 | gap: 30px; 5 | flex-wrap: wrap; 6 | } 7 | 8 | .item { 9 | display: flex; 10 | flex-direction: column; 11 | justify-content: center; 12 | align-items: center; 13 | height: 20em; 14 | width: 25em; 15 | background-color: #d9c147; 16 | border: 2px solid #d1b000; 17 | box-shadow: 0px 3px 5px 1px #636363; 18 | border-radius: 10px; 19 | margin: 0.125em; 20 | padding: 2em; 21 | } 22 | -------------------------------------------------------------------------------- /src/data/data.ts: -------------------------------------------------------------------------------- 1 | export const data = [ 2 | { id: 1, name: "TodoList", icon: "" }, 3 | { id: 2, name: "Calendar", icon: "" }, 4 | { id: 3, name: "MealPrep", icon: "" }, 5 | { id: 4, name: "Workout", icon: "" }, 6 | { id: 5, name: "Grocery", icon: "" }, 7 | { id: 6, name: "Chores", icon: "" }, 8 | { id: 7, name: "Allowance", icon: "" }, 9 | { id: 8, name: "Budget", icon: "" }, 10 | { id: 9, name: "Reminders", icon: "" }, 11 | { id: 10, name: "Events", icon: "" }, 12 | { id: 11, name: "Chat", icon: "" }, 13 | ]; 14 | -------------------------------------------------------------------------------- /.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 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /src/app/(main)/(about)/about/About.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 75%; 3 | margin: 3em auto; 4 | padding: 1em; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | 10 | background-color: #d9c147; 11 | border: 2px solid #d1b000; 12 | box-shadow: 0px 3px 5px 1px #636363; 13 | border-radius: 10px; 14 | } 15 | 16 | .container h2 { 17 | text-decoration: underline; 18 | padding-bottom: 1em; 19 | } 20 | .container p { 21 | font-size: 1.3em; 22 | padding: 1em; 23 | border: 2px solid #d1b000; 24 | } 25 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/(main)/_components/Navbar/Navbar.module.css: -------------------------------------------------------------------------------- 1 | .navbarContainer { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | height: 3.5rem; 6 | padding: 0 1em; 7 | border-bottom: 2px solid rgba(0, 0, 0, 0.432); 8 | } 9 | 10 | .links { 11 | padding: 10px; 12 | border-left: 1px solid gray; 13 | } 14 | 15 | .loginButton { 16 | padding: 0.4em; 17 | height: 3em; 18 | width: 3.75em; 19 | background-color: #d9c147; 20 | border: 2px solid #d1b000; 21 | box-shadow: 0px 3px 5px 1px #636363; 22 | border-radius: 10px; 23 | margin: 0 1em; 24 | } 25 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/CreateTodoForm/CreateTodo.module.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | gap: 0.2em; 4 | margin: 1em 0; 5 | } 6 | 7 | .formInput { 8 | border-radius: 20px; 9 | padding: 0.2em; 10 | text-align: center; 11 | } 12 | 13 | .formButton { 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | height: 2em; 18 | width: 3.75em; 19 | background-color: #d9c147; 20 | border: 2px solid #d1b000; 21 | box-shadow: 0px 3px 5px 1px #636363; 22 | border-radius: 10px; 23 | margin: 0.125em; 24 | cursor: pointer; 25 | } 26 | -------------------------------------------------------------------------------- /src/redux/store.ts: -------------------------------------------------------------------------------- 1 | import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit"; 2 | 3 | import todosReducer from "@/slices/todosSlice"; 4 | import { useDispatch } from "react-redux"; 5 | 6 | export const store = configureStore({ 7 | reducer: { todos: todosReducer }, 8 | }); 9 | 10 | export type AppStore = typeof store; 11 | export type AppDispatch = typeof store.dispatch; 12 | export type RootState = ReturnType; 13 | export type AppThunk = ThunkAction< 14 | ReturnType, 15 | RootState, 16 | unknown, 17 | Action 18 | >; 19 | 20 | export const useAppDispatch = () => useDispatch(); 21 | -------------------------------------------------------------------------------- /src/app/(main)/(capstone)/capstone/capstone.module.css: -------------------------------------------------------------------------------- 1 | .capstoneContainer { 2 | width: 1200px; 3 | margin: auto; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | } 8 | 9 | .capstoneContainer h1 { 10 | text-decoration: underline; 11 | padding-bottom: 0.5em; 12 | } 13 | .capstoneContainer h2 { 14 | text-decoration: underline; 15 | padding: 0.5em 0; 16 | color: rgb(126, 35, 35); 17 | } 18 | .capstoneContainer h3 { 19 | text-decoration: underline; 20 | padding: 1.5em 0 0.5em 0; 21 | } 22 | .capstoneContainer p { 23 | background-color: rgba(0, 0, 0, 0.445); 24 | color: white; 25 | padding: 0.3em; 26 | } 27 | -------------------------------------------------------------------------------- /src/app/(dashboard)/dashboard/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import FeatureButton from "../_components/FeatureButton/FeatureButton"; 4 | import FeatureMain from "../_components/FeatureMain/FeatureMain"; 5 | import FeatureButtonContainer from "../_components/FeatureButtonContainer/FeatureButtonContainer"; 6 | 7 | import { useState } from "react"; 8 | 9 | export default function DashHome() { 10 | const [featureWindow, setFeatureWindow] = useState(false); 11 | return ( 12 |
13 | 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./src/*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /middleware.ts: -------------------------------------------------------------------------------- 1 | import { type NextRequest } from "next/server"; 2 | import { updateSession } from "@/utils/supabase/middleware"; 3 | 4 | export async function middleware(request: NextRequest) { 5 | return await updateSession(request); 6 | } 7 | 8 | export const config = { 9 | matcher: [ 10 | /* 11 | * Match all request paths except for the ones starting with: 12 | * - _next/static (static files) 13 | * - _next/image (image optimization files) 14 | * - favicon.ico (favicon file) 15 | * Feel free to modify this pattern to include more paths. 16 | */ 17 | "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", 18 | ], 19 | }; 20 | -------------------------------------------------------------------------------- /src/app/(main)/styles/mainStyles.module.css: -------------------------------------------------------------------------------- 1 | .mainContainer { 2 | display: flex; 3 | } 4 | .shopLink { 5 | padding: 1em; 6 | height: 3.75em; 7 | width: 3.75em; 8 | background-color: #d9c147; 9 | border: 2px solid #d1b000; 10 | box-shadow: 0px 3px 5px 1px #636363; 11 | border-radius: 10px; 12 | margin: 0.125em; 13 | } 14 | 15 | .leftPanel { 16 | width: 50%; 17 | height: 80vh; 18 | border: 1px solid black; 19 | align-content: center; 20 | text-align: center; 21 | } 22 | 23 | .leftPanel p { 24 | margin-bottom: 2em; 25 | } 26 | .rightPanel { 27 | width: 50%; 28 | height: 80vh; 29 | border: 1px solid black; 30 | align-content: center; 31 | justify-self: center; 32 | } 33 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/Sidebar/SideBarStyles.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 15%; 3 | height: 100vh; 4 | border-right: 1px solid #cfae01; 5 | display: flex; 6 | flex-direction: column; 7 | align-items: center; 8 | } 9 | .spacer { 10 | width: 75%; 11 | height: 0.1rem; 12 | background-color: #000; 13 | border-radius: 50%; 14 | } 15 | .logo { 16 | width: 100%; 17 | height: 2.9rem; 18 | /* border: 1px solid #cfae01; */ 19 | display: flex; 20 | justify-content: center; 21 | align-content: center; 22 | } 23 | 24 | .linkStyles { 25 | align-content: center; 26 | text-align: center; 27 | height: 3rem; 28 | width: 100%; 29 | 30 | border-bottom: 1px solid #666666; 31 | } 32 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureButton/FeatureButton.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./FeatureButton.module.css"; 2 | import { type featureWindowProps } from "@/lib/types"; 3 | type FeatureButtonProps = { 4 | feature: string; 5 | showFeatureWindow: featureWindowProps; 6 | setFeature: React.Dispatch>; 7 | }; 8 | function FeatureButton({ 9 | feature, 10 | showFeatureWindow, 11 | setFeature, 12 | }: FeatureButtonProps) { 13 | return ( 14 |
{ 16 | showFeatureWindow(true); 17 | setFeature(feature); 18 | }} 19 | className={styles.container}> 20 | {feature} 21 |
22 | ); 23 | } 24 | export default FeatureButton; 25 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/Sidebar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import styles from "./SideBarStyles.module.css"; 3 | 4 | function Sidebar() { 5 | return ( 6 |
7 |
8 | 9 |

LOGO

10 | 11 |
12 | 13 | HOME 14 | SHOP 15 | SETTINGS 16 |
17 | ); 18 | } 19 | export default Sidebar; 20 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | import "./globals.css"; 4 | import MainProvider from "@/components/MainProvider"; 5 | 6 | 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Create Next App", 11 | description: "Generated by create next app", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | 21 | 22 | 23 | 24 |
{children}
25 |
26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeatureMain.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | background-color: rgba(0, 0, 0, 0.479); 3 | height: 80rem; 4 | width: 110rem; 5 | display: flex; 6 | justify-content: center; 7 | position: fixed; 8 | top: 4.5rem; 9 | left: 20; 10 | cursor: pointer; 11 | z-index: 9; 12 | } 13 | 14 | .featureMenu { 15 | display: flex; 16 | flex-direction: column; 17 | background-color: #dcc856ce; 18 | width: 75%; 19 | height: 600px; 20 | margin-top: 50px; 21 | cursor: default; 22 | border-radius: 2em; 23 | } 24 | .closeIcon { 25 | align-self: flex-end; 26 | justify-content: center; 27 | padding: 15px 25px; 28 | position: fixed; 29 | cursor: pointer; 30 | } 31 | 32 | .featureGrid { 33 | display: flex; 34 | gap: 25px; 35 | margin: 100px; 36 | } 37 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/auth/auth.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 25rem; 3 | height: 15rem; 4 | margin: 6em auto; 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | 10 | background-color: #d9c147; 11 | border: 2px solid #d1b000; 12 | box-shadow: 0px 3px 5px 1px #636363; 13 | border-radius: 10px; 14 | } 15 | 16 | .loginButton { 17 | display: flex; 18 | justify-content: center; 19 | align-items: center; 20 | height: 2em; 21 | width: 3.75em; 22 | background-color: #d9c147; 23 | border: 2px solid #d1b000; 24 | box-shadow: 0px 3px 5px 1px #636363; 25 | border-radius: 10px; 26 | margin: 1em; 27 | cursor: pointer; 28 | } 29 | 30 | .form { 31 | display: flex; 32 | flex-direction: column; 33 | } 34 | 35 | .buttonContainer { 36 | display: flex; 37 | } 38 | -------------------------------------------------------------------------------- /src/app/(main)/_components/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import styles from "./Footer.module.css"; 3 | 4 | function Footer() { 5 | return ( 6 |
7 |

Copywrite Mays88 2024

8 | 9 |
10 | 11 | Dashboard 12 | 13 | 14 | Shop 15 | 16 | 17 | About 18 | 19 | 20 | Capstone 21 | 22 |
23 |
24 | ); 25 | } 26 | export default Footer; 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## About the project 2 | 3 | For my capstone, I decided to create a Life management and Family management App. Due to requirements and time, I will be presenting on the functions of my Todo List Feature. I have always wanted to create an App that I can grow with and I believe an app as diverse is this is perfect to increase my skills as a Software Engineer. 4 | 5 | ## Project Links 6 | 7 | This is the [Production Build](https://sea-lion-app-ihyvc.ondigitalocean.app/) and production repo, [Frontend Repo](https://github.com/mays88/Capstone) hosted using the App platform of Digital Ocean and the Wireframe was done with [`Figma`](https://www.figma.com/design/gd9B2rFFnODgm3ugErgRHz/TBT?node-id=0-1&t=anhqygOV4PQk2tit-1). My backend API was also launched with Digital Ocean App Platform [`Node/Express API`](https://oyster-app-3xg9q.ondigitalocean.app/api/v1/todos), [API Repo](https://github.com/mays88/node-traffic-controller). 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tbt", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@reduxjs/toolkit": "^2.2.5", 13 | "@supabase/ssr": "^0.3.0", 14 | "@supabase/supabase-js": "^2.43.5", 15 | "@types/react-redux": "^7.1.33", 16 | "axios": "^1.7.2", 17 | "next": "14.2.3", 18 | "next-redux-wrapper": "^8.1.0", 19 | "react": "^18", 20 | "react-dom": "^18", 21 | "react-redux": "^9.1.2", 22 | "redux": "^5.0.1", 23 | "sharp": "^0.33.4", 24 | "styled-components": "^6.1.11" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^20", 28 | "@types/react": "^18", 29 | "@types/react-dom": "^18", 30 | "eslint": "^8", 31 | "eslint-config-next": "14.2.3", 32 | "typescript": "^5" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/app/(main)/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import styles from "./styles/mainStyles.module.css"; 3 | import Image from "next/image"; 4 | import HeroImage from "./assets/hero.png"; 5 | 6 | export default async function Home() { 7 | // const recipes = await getRecipes(); 8 | return ( 9 |
10 |
11 |

12 | Welcome to
13 | The Life and Family management app. 14 |

15 |

Where Unity meets Progression

16 | 17 | Explore Products 18 | 19 |
20 | 21 |
22 | Hero 23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /src/app/(dashboard)/layout.tsx: -------------------------------------------------------------------------------- 1 | import Header from "./_components/Header/Header"; 2 | import NewsScroll from "./_components/NewsScroll/NewsScroll"; 3 | import Sidebar from "./_components/Sidebar/Sidebar"; 4 | import styles from "./styles/main.module.css"; 5 | import { redirect } from "next/navigation"; 6 | 7 | import { createClient } from "@/utils/supabase/server"; 8 | 9 | export default async function DashLayout({ children }: { children: any }) { 10 | const supabase = createClient(); 11 | 12 | const { data, error } = await supabase.auth.getUser(); 13 | if (error || !data?.user) { 14 | redirect("/auth"); 15 | } 16 | return ( 17 | <> 18 |
19 | 20 |
21 |
22 | 23 | {children} 24 |
25 |
26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /src/app/(main)/_components/Navbar/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import styles from "./Navbar.module.css"; 3 | 4 | function Navbar() { 5 | return ( 6 |
7 | 8 |

LOGO

9 | 10 | 11 |
12 | 13 | Dashboard 14 | 15 | 16 | Shop 17 | 18 | 19 | About 20 | 21 | 22 | Capstone 23 | 24 | Sign-In 25 |
26 |
27 | ); 28 | } 29 | export default Navbar; 30 | -------------------------------------------------------------------------------- /src/api/todoApi.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export async function createTask(task: string) { 4 | try { 5 | const response = await axios.post( 6 | `https://oyster-app-3xg9q.ondigitalocean.app/api/v1/todos`, 7 | { userId: 88, completed: false, title: task } 8 | ); 9 | 10 | return response.data; 11 | } catch (error) { 12 | console.error(error); 13 | } 14 | } 15 | 16 | export async function updTask(task: any) { 17 | try { 18 | const response = await axios.patch( 19 | `https://oyster-app-3xg9q.ondigitalocean.app/api/v1/todos/${task.id}`, 20 | { ...task, title: task.title } 21 | ); 22 | 23 | return response.data; 24 | } catch (error) { 25 | console.error(error); 26 | } 27 | } 28 | export async function deleteTask(taskId: any) { 29 | try { 30 | const response = await axios.delete( 31 | `https://oyster-app-3xg9q.ondigitalocean.app/api/v1/todos/${taskId}` 32 | ); 33 | 34 | return response.data; 35 | } catch (error) { 36 | console.error(error); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/app/(main)/(about)/about/page.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./About.module.css"; 2 | 3 | export default function About() { 4 | return ( 5 |
6 |

About Me

7 |

8 | An initiative-taking College educated Air Force Veteran 9 | Self-taught Full-Stack Developer. A passionate professional for 10 | creating responsive and user-friendly web applications using 11 | cutting-edge technologies such as React, Next.js, TypeScript, 12 | Supabase, and PostgreSQL. Expertise includes clear cut and 13 | creative problem-solving abilities complimented with a keen eye 14 | to detail. Adaptable, smart, resourceful, and disciplined when 15 | addressing technical issues. Robust leadership qualities 16 | complemented with a great ability to effectively communicate and 17 | influence people in a positive demeanor. A strong troubleshooter 18 | with a keen strategic mindset when planning and executing 19 | directives 20 |

21 |
22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/auth/page.tsx: -------------------------------------------------------------------------------- 1 | import { login, signup } from "./actions"; 2 | import styles from "./auth.module.css"; 3 | export default function LoginPage() { 4 | return ( 5 |
6 |

Login/Sign Up

7 |
8 | 9 | 10 | 11 | 12 |
13 | 19 | 25 |
26 |
27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/FeaturesSection.tsx: -------------------------------------------------------------------------------- 1 | import Feature from "./Feature/Feature"; 2 | import styles from "./FeaturesSectionStyles.module.css"; 3 | import { data } from "@/data/data"; 4 | type FeatureSectionProps = { 5 | feature: string; 6 | }; 7 | 8 | function FeaturesSection({ feature }: FeatureSectionProps) { 9 | return ( 10 | <> 11 | {data.map((item) => { 12 | if (item.name === feature) { 13 | return ( 14 |
15 |
16 | Feature Settings 17 |
18 |
19 | 20 |
21 |
22 | Quick Launch 23 |
24 |
25 | ); 26 | } 27 | })} 28 | 29 | ); 30 | } 31 | export default FeaturesSection; 32 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/auth/confirm/route.ts: -------------------------------------------------------------------------------- 1 | import { type EmailOtpType } from "@supabase/supabase-js"; 2 | import { type NextRequest, NextResponse } from "next/server"; 3 | 4 | import { createClient } from "@/utils/supabase/server"; 5 | 6 | export async function GET(request: NextRequest) { 7 | const { searchParams } = new URL(request.url); 8 | const token_hash = searchParams.get("token_hash"); 9 | const type = searchParams.get("type") as EmailOtpType | null; 10 | const next = searchParams.get("next") ?? "/"; 11 | 12 | const redirectTo = request.nextUrl.clone(); 13 | redirectTo.pathname = next; 14 | redirectTo.searchParams.delete("token_hash"); 15 | redirectTo.searchParams.delete("type"); 16 | 17 | if (token_hash && type) { 18 | const supabase = createClient(); 19 | 20 | const { error } = await supabase.auth.verifyOtp({ 21 | type, 22 | token_hash, 23 | }); 24 | if (!error) { 25 | redirectTo.searchParams.delete("next"); 26 | return NextResponse.redirect(redirectTo); 27 | } 28 | } 29 | 30 | // return the user to an error page with some instructions 31 | redirectTo.pathname = "/error"; 32 | return NextResponse.redirect(redirectTo); 33 | } 34 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/Task/Task.module.css: -------------------------------------------------------------------------------- 1 | .formContainer { 2 | border-radius: 3em; 3 | background-color: #d9c147ad; 4 | box-shadow: 0px 3px 3px 2px#d1b000; 5 | display: flex; 6 | width: 95%; 7 | height: 3em; 8 | margin: 0.5em 0; 9 | justify-content: space-between; 10 | align-items: center; 11 | list-style: none; 12 | overflow: hidden; 13 | } 14 | .buttonContainer { 15 | display: flex; 16 | } 17 | .form { 18 | display: flex; 19 | width: 100%; 20 | justify-content: space-between; 21 | padding: 0 1em; 22 | } 23 | 24 | .formButton { 25 | display: flex; 26 | justify-content: center; 27 | align-items: center; 28 | height: 2em; 29 | width: 3.75em; 30 | background-color: #d9c147; 31 | border: 2px solid #d1b000; 32 | box-shadow: 0px 3px 5px 1px #636363; 33 | border-radius: 10px; 34 | margin: 0.5em; 35 | cursor: pointer; 36 | } 37 | 38 | .taskTitle { 39 | display: flex; 40 | align-items: center; 41 | margin: 0 0.5em; 42 | } 43 | 44 | .modal { 45 | display: flex; 46 | align-items: center; 47 | justify-content: center; 48 | position: absolute; 49 | top: 0; 50 | left: 0; 51 | width: 100%; 52 | height: 100%; 53 | } 54 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/UpdateTaskForm/UpdateTaskForm.module.css: -------------------------------------------------------------------------------- 1 | .modal { 2 | display: flex; 3 | flex-direction: column; 4 | align-items: center; 5 | justify-content: center; 6 | position: absolute; 7 | top: 0; 8 | left: 0; 9 | width: 100%; 10 | height: 100%; 11 | z-index: 9; 12 | border: 1px solid #000; 13 | background-color: #414141b0; 14 | } 15 | 16 | .formInput { 17 | border-radius: 1em; 18 | padding: 1em; 19 | width: 20em; 20 | } 21 | .buttonContainer { 22 | display: flex; 23 | justify-content: center; 24 | gap: 0.5em; 25 | margin: 1em; 26 | } 27 | 28 | .formSubmit { 29 | display: flex; 30 | justify-content: center; 31 | align-items: center; 32 | height: 2em; 33 | width: 3.75em; 34 | background-color: #d9c147; 35 | border: 2px solid #d1b000; 36 | box-shadow: 0px 3px 5px 1px #636363; 37 | border-radius: 10px; 38 | margin: 0.125em; 39 | } 40 | 41 | .closeIcon { 42 | display: flex; 43 | justify-content: center; 44 | align-items: center; 45 | height: 2em; 46 | width: 3.75em; 47 | background-color: #d9c147; 48 | border: 2px solid #d1b000; 49 | box-shadow: 0px 3px 5px 1px #636363; 50 | border-radius: 10px; 51 | margin: 0.125em; 52 | } 53 | -------------------------------------------------------------------------------- /public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/CreateTodoForm/index.tsx: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from "react-redux"; 2 | import { addTask } from "@/slices/todosSlice"; 3 | import { useState } from "react"; 4 | import styles from "./CreateTodo.module.css"; 5 | import { createTask } from "@/api/todoApi"; 6 | function CreateTodoForm() { 7 | const dispatch = useDispatch(); 8 | const [content, setContent] = useState(""); 9 | 10 | const handleSubmit = async (e: React.FormEvent) => { 11 | e.preventDefault(); 12 | if (!content) { 13 | return; 14 | } 15 | 16 | const data = await createTask(content); 17 | 18 | dispatch(addTask(data.data.todo)); 19 | setContent(""); 20 | }; 21 | return ( 22 |
23 | { 30 | setContent(e.target.value); 31 | }} 32 | className={styles.formInput} 33 | /> 34 | 35 |
36 | ); 37 | } 38 | export default CreateTodoForm; 39 | -------------------------------------------------------------------------------- /src/app/(main)/(auth)/auth/actions/index.ts: -------------------------------------------------------------------------------- 1 | "use server"; 2 | 3 | import { revalidatePath } from "next/cache"; 4 | import { redirect } from "next/navigation"; 5 | 6 | import { createClient } from "@/utils/supabase/server"; 7 | 8 | export async function login(formData: FormData) { 9 | const supabase = createClient(); 10 | 11 | // type-casting here for convenience 12 | // in practice, you should validate your inputs 13 | const data = { 14 | email: formData.get("email") as string, 15 | password: formData.get("password") as string, 16 | }; 17 | 18 | const { error } = await supabase.auth.signInWithPassword({ 19 | email: data.email, 20 | password: data.password, 21 | }); 22 | console.log(error); 23 | if (error) { 24 | redirect("/error"); 25 | } 26 | 27 | revalidatePath("/", "layout"); 28 | redirect("/"); 29 | } 30 | 31 | export async function signup(formData: FormData) { 32 | const supabase = createClient(); 33 | 34 | // type-casting here for convenience 35 | // in practice, you should validate your inputs 36 | const data = { 37 | email: formData.get("email") as string, 38 | password: formData.get("password") as string, 39 | }; 40 | 41 | const { error } = await supabase.auth.signUp(data); 42 | 43 | if (error) { 44 | redirect("/error"); 45 | } 46 | 47 | revalidatePath("/", "layout"); 48 | redirect("/"); 49 | } 50 | -------------------------------------------------------------------------------- /src/utils/supabase/server.ts: -------------------------------------------------------------------------------- 1 | import { createServerClient, type CookieOptions } from "@supabase/ssr"; 2 | import { cookies } from "next/headers"; 3 | 4 | export function createClient() { 5 | const cookieStore = cookies(); 6 | 7 | return createServerClient( 8 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 9 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, 10 | { 11 | cookies: { 12 | get(name: string) { 13 | return cookieStore.get(name)?.value; 14 | }, 15 | set(name: string, value: string, options: CookieOptions) { 16 | try { 17 | cookieStore.set({ name, value, ...options }); 18 | } catch (error) { 19 | // The `set` method was called from a Server Component. 20 | // This can be ignored if you have middleware refreshing 21 | // user sessions. 22 | } 23 | }, 24 | remove(name: string, options: CookieOptions) { 25 | try { 26 | cookieStore.set({ name, value: "", ...options }); 27 | } catch (error) { 28 | // The `delete` method was called from a Server Component. 29 | // This can be ignored if you have middleware refreshing 30 | // user sessions. 31 | } 32 | }, 33 | }, 34 | } 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Feature.tsx: -------------------------------------------------------------------------------- 1 | import TodoList from "./Features/TodoList/TodoList"; 2 | 3 | type FeatureProps = { 4 | feature: string; 5 | }; 6 | 7 | function Feature({ feature }: FeatureProps) { 8 | switch (feature) { 9 | case "TodoList": 10 | return ( 11 | <> 12 | 13 | 14 | ); 15 | 16 | case "Calendar": 17 | return
{feature + " Welcome to the Feature component"}
; 18 | 19 | case "MealPrep": 20 | return
{feature + " Welcome to the Feature component"}
; 21 | 22 | case "Workout": 23 | return
{feature + " Welcome to the Feature component"}
; 24 | 25 | case "Grocery": 26 | return
{feature + " Welcome to the Feature component"}
; 27 | 28 | case "Chores": 29 | return
{feature + " Welcome to the Feature component"}
; 30 | 31 | case "Allowance": 32 | return
{feature + " Welcome to the Feature component"}
; 33 | case "Budget": 34 | return
{feature + " Welcome to the Feature component"}
; 35 | case "Reminders": 36 | return
{feature + " Welcome to the Feature component"}
; 37 | case "Message/Linkup/Chat": 38 | return
{feature + " Welcome to the Feature component"}
; 39 | 40 | default: 41 | break; 42 | } 43 | } 44 | export default Feature; 45 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/TodoList.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useEffect } from "react"; 3 | import { RootState, useAppDispatch } from "@/redux/store"; 4 | import { fetchData } from "@/slices/todosSlice"; 5 | import { useSelector, useDispatch } from "react-redux"; 6 | import { selectTodos } from "@/slices/todosSlice"; 7 | import Task from "./Task"; 8 | import CreateTodoForm from "./CreateTodoForm"; 9 | // import { v4 as uuidv4 } from "uuid"; 10 | import styles from "./TodoList.module.css"; 11 | 12 | function TodoList() { 13 | const dispatch = useAppDispatch(); 14 | const { data, loading, error } = useSelector( 15 | (state: RootState) => state.todos 16 | ); 17 | 18 | useEffect(() => { 19 | dispatch(fetchData()); 20 | }, [dispatch]); 21 | 22 | const todos = useSelector(selectTodos); 23 | 24 | return ( 25 | <> 26 |
27 |

Todo List

28 | 29 | 30 | {todos && 31 | todos.map((item: any) => { 32 | return ( 33 | 39 | ); 40 | })} 41 |
42 | 43 | ); 44 | } 45 | export default TodoList; 46 | -------------------------------------------------------------------------------- /src/app/(main)/(shop)/_components/Shop.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./componentStyles.module.css"; 2 | 3 | const items = [ 4 | { 5 | id: 1, 6 | name: "T-Shirt", 7 | description: "T-Shirt made of clouds from Olympus, approved by Zues.", 8 | }, 9 | { 10 | id: 2, 11 | name: "Pants", 12 | description: "Pants made of clouds from Olympus, approved by Zues.", 13 | }, 14 | { 15 | id: 3, 16 | name: "Shoes", 17 | description: "Shoes made of clouds from Olympus, approved by Zues.", 18 | }, 19 | { 20 | id: 4, 21 | name: "Belt", 22 | description: "Belt made of clouds from Olympus, approved by Zues.", 23 | }, 24 | { 25 | id: 5, 26 | name: "Socks", 27 | description: "Socks made of clouds from Olympus, approved by Zues.", 28 | }, 29 | { 30 | id: 6, 31 | name: "Glasses", 32 | description: "Glasses made of clouds from Olympus, approved by Zues.", 33 | }, 34 | { 35 | id: 7, 36 | name: "Hat", 37 | description: "Hat made of clouds from Olympus, approved by Zues.", 38 | }, 39 | ]; 40 | 41 | function Shop() { 42 | return ( 43 | <> 44 |
45 | {items.map((item) => { 46 | return ( 47 |
48 |

{item.name}

49 |

{item.description}

50 |
51 | ); 52 | })} 53 |
54 | 55 | ); 56 | } 57 | export default Shop; 58 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeatureMain.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useState } from "react"; 4 | import styles from "./FeatureMain.module.css"; 5 | import FeaturesSection from "./FeaturesSection/FeaturesSection"; 6 | import { featureWindowProps } from "@/lib/types"; 7 | import FeatureButtonContainer from "../FeatureButtonContainer/FeatureButtonContainer"; 8 | import FeatureButton from "../FeatureButton/FeatureButton"; 9 | 10 | import { data } from "@/data/data"; 11 | type FeatureMainProps = { 12 | showFeatureWindow: featureWindowProps; 13 | showWindow: boolean; 14 | }; 15 | function FeatureMain({ showWindow, showFeatureWindow }: FeatureMainProps) { 16 | const [feature, setFeature] = useState(""); 17 | return ( 18 | <> 19 | 20 | {data.map((item) => { 21 | return ( 22 | 28 | ); 29 | })} 30 | 31 |
32 | {showWindow && ( 33 |
34 |
35 |
showFeatureWindow(false)} 37 | className={styles.closeIcon}> 38 | X 39 |
40 | 41 | 42 |
43 |
44 | )} 45 |
46 | 47 | ); 48 | } 49 | export default FeatureMain; 50 | -------------------------------------------------------------------------------- /src/utils/supabase/middleware.ts: -------------------------------------------------------------------------------- 1 | import { createServerClient, type CookieOptions } from "@supabase/ssr"; 2 | import { NextResponse, type NextRequest } from "next/server"; 3 | 4 | export async function updateSession(request: NextRequest) { 5 | let response = NextResponse.next({ 6 | request: { 7 | headers: request.headers, 8 | }, 9 | }); 10 | 11 | const supabase = createServerClient( 12 | process.env.NEXT_PUBLIC_SUPABASE_URL!, 13 | process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, 14 | { 15 | cookies: { 16 | get(name: string) { 17 | return request.cookies.get(name)?.value; 18 | }, 19 | set(name: string, value: string, options: CookieOptions) { 20 | request.cookies.set({ 21 | name, 22 | value, 23 | ...options, 24 | }); 25 | response = NextResponse.next({ 26 | request: { 27 | headers: request.headers, 28 | }, 29 | }); 30 | response.cookies.set({ 31 | name, 32 | value, 33 | ...options, 34 | }); 35 | }, 36 | remove(name: string, options: CookieOptions) { 37 | request.cookies.set({ 38 | name, 39 | value: "", 40 | ...options, 41 | }); 42 | response = NextResponse.next({ 43 | request: { 44 | headers: request.headers, 45 | }, 46 | }); 47 | response.cookies.set({ 48 | name, 49 | value: "", 50 | ...options, 51 | }); 52 | }, 53 | }, 54 | } 55 | ); 56 | 57 | await supabase.auth.getUser(); 58 | 59 | return response; 60 | } 61 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/UpdateTaskForm/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { useEffect, useState } from "react"; 3 | import { useDispatch } from "react-redux"; 4 | // import { Button } from "../styles/Button.styled"; 5 | // import { IoClose } from "react-icons/io5"; 6 | // import { Modal } from "../styles/Modal.styled"; 7 | import { updateTask } from "@/slices/todosSlice"; 8 | import styles from "./UpdateTaskForm.module.css"; 9 | import { updTask } from "@/api/todoApi"; 10 | function UpdateTaskForm({ 11 | task, 12 | setShowModal, 13 | }: { 14 | task: any; 15 | setShowModal: any; 16 | }) { 17 | const dispatch = useDispatch(); 18 | const [newTaskContent, setNewTaskContent] = useState(task.title); 19 | const handleSubmit = async (e: React.FormEvent) => { 20 | e.preventDefault(); 21 | 22 | const res = await updTask(task); 23 | 24 | dispatch(updateTask({ id: task._id, title: newTaskContent })); 25 | 26 | setShowModal(false); 27 | }; 28 | 29 | return ( 30 |
31 |
32 | { 40 | setNewTaskContent(e.target.value); 41 | }} 42 | /> 43 |
44 | 49 | 56 |
57 |
58 |
59 | ); 60 | } 61 | export default UpdateTaskForm; 62 | -------------------------------------------------------------------------------- /src/app/(dashboard)/_components/FeatureMain/FeaturesSection/Feature/Features/TodoList/Task/index.tsx: -------------------------------------------------------------------------------- 1 | import { useDispatch } from "react-redux"; 2 | import { removeTask } from "@/slices/todosSlice"; 3 | import { useState } from "react"; 4 | import { createPortal } from "react-dom"; 5 | import UpdateTaskForm from "../UpdateTaskForm"; 6 | import styles from "./Task.module.css"; 7 | 8 | type TaskProps = { 9 | task: any; 10 | }; 11 | 12 | function Task({ task }: TaskProps) { 13 | const [showModal, setShowModal] = useState(false); 14 | const [checked, setChecked] = useState(false); 15 | const dispatch = useDispatch(); 16 | const checkHandler = () => { 17 | setChecked(!checked); 18 | }; 19 | 20 | return ( 21 | <> 22 | {showModal && 23 | createPortal( 24 | , 25 | document.body 26 | )} 27 | 28 |
29 |
35 | 42 | 43 |

{task.title}

44 |
45 | 54 | {checked && ( 55 | 65 | )} 66 |
67 |
68 |
69 | 70 | ); 71 | } 72 | export default Task; 73 | -------------------------------------------------------------------------------- /src/slices/todosSlice.ts: -------------------------------------------------------------------------------- 1 | import { PayloadAction, createAsyncThunk, createSlice } from "@reduxjs/toolkit"; 2 | 3 | import { createTask, deleteTask, updTask } from "@/api/todoApi"; 4 | import { AppThunk, RootState } from "@/redux/store"; 5 | import axios from "axios"; 6 | 7 | type MyState = { 8 | data: any; 9 | loading: boolean; 10 | error: string | null; 11 | }; 12 | 13 | const initialState: MyState = { 14 | data: null, 15 | loading: false, 16 | error: null, 17 | }; 18 | // const todos = getTask(); 19 | export const fetchData = (): AppThunk => async (dispatch) => { 20 | try { 21 | dispatch(fetchDataStart()); 22 | const data = await fetchTodos(); // Replace this with your promise function 23 | dispatch(fetchDataSuccess(data)); 24 | } catch (error: any) { 25 | dispatch(fetchDataFailure(error.message)); 26 | } 27 | }; 28 | export const fetchTodos = async () => { 29 | try { 30 | const response = await axios.get( 31 | "https://oyster-app-3xg9q.ondigitalocean.app/api/v1/todos" 32 | ); 33 | 34 | return response.data.data.todos; 35 | } catch (error) { 36 | console.error(error); 37 | } 38 | }; 39 | 40 | const todosSlice = createSlice({ 41 | name: "todos", 42 | initialState, 43 | reducers: { 44 | addTask: (state, action) => { 45 | state.data.unshift(action.payload); 46 | }, 47 | removeTask: (state, action) => { 48 | deleteTask(action.payload.id); 49 | }, 50 | updateTask: (state, action) => { 51 | updTask(action.payload); 52 | 53 | // return { 54 | // ...state, 55 | // todos: [...state.data, action.payload], 56 | // }; 57 | state.data.map((t: any) => { 58 | if (t._id === action.payload.id) { 59 | t.title = action.payload.title; 60 | } else return t; 61 | }); 62 | }, 63 | fetchDataStart(state) { 64 | state.loading = true; 65 | state.error = null; 66 | }, 67 | fetchDataSuccess(state, action: PayloadAction) { 68 | state.loading = false; 69 | state.data = action.payload; 70 | }, 71 | fetchDataFailure(state, action: PayloadAction) { 72 | state.loading = false; 73 | state.error = action.payload; 74 | }, 75 | // editTask: 76 | // increase: (state, action) => { 77 | // state.count += action.payload; 78 | // }, 79 | // decrease: (state, action) => { 80 | // state.count -= action.payload; 81 | // }, 82 | }, 83 | }); 84 | 85 | // export main reducer 86 | 87 | export default todosSlice.reducer; 88 | 89 | // Selectors 90 | export const selectTodos = (state: RootState) => state.todos.data; 91 | 92 | // Actions 93 | export const { 94 | addTask, 95 | removeTask, 96 | updateTask, 97 | fetchDataStart, 98 | fetchDataSuccess, 99 | fetchDataFailure, 100 | } = todosSlice.actions; 101 | -------------------------------------------------------------------------------- /src/app/(main)/(capstone)/capstone/page.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import styles from "./capstone.module.css"; 3 | 4 | export default function Capstone() { 5 | return ( 6 |
7 |

Requirements

8 |

(20%) Project Structure, Standardization, and Convention

9 |

10 | Project is organized into appropriate files and directories, 11 | following best practices. 12 |

13 |

Project is organized in a structured way.

14 |

Project contains an appropriate level of comments.

15 |

16 | Most aspects of the project contain names that describes the 17 | data, comments will be placed where needed 18 |

19 |

20 | Project is pushed to GitHub, and contains a README file that 21 | documents the project, including an overall description of the 22 | project. 23 |

24 |

25 | Updated README with a description of the project as well as 26 | links to my deployments. 27 |

28 |

29 | Standard naming conventions are used throughout the project. 30 |

31 |

32 | I believe that the naming conventions are standard and ordered 33 | in an understandable way. 34 |

35 |

36 | Ensure that the program runs without errors (comment out things 37 | that do not work, and explain your blockers - you can still 38 | receive partial credit). 39 |

40 |

41 | Level of effort displayed in creativity, presentation, and user 42 | experience. 43 |

44 |

45 | This project was well planned and thought out, As I build, I 46 | learn to do things in more efficient ways 47 |

48 |

(12%) Core JavaScript

49 |

Demonstrate proper usage of ES6 syntax and tools.

50 |

51 | My ability to construct the current site displays that I have an 52 | understanding of the proper usuage of ES6 syntax and tools. 53 |

54 |

Use functions and classes to adhere to the DRY principle.

55 |

56 | Everywhere that I noticed, I created separate components or 57 | functions to extract code to prevent from repeating myself. 58 |

59 |

Use Promises and async/await, where appropriate.

60 |

I used async/await during fetching of data from my api.

61 |

Use Axios or fetch to retrieve data from an API.

62 |

I used Axioss during fetching of data from my api

63 |

Use sound programming logic throughout the application.

64 |

65 | Considering my code organization and structure, I believe that I 66 | am using sound programming logic 67 |

68 |

Use appropriate exception handling.

69 |

70 | In order to handle exceptions, I used conditionals and try/catch 71 | to handle exceptions 72 |

73 |

(9%) Database

74 |

Use MongoDB to create a database for your application.

75 |

Used MongoDB to create the Capstone database for my project

76 |

Apply appropriate indexes to your database collections.

77 |

Added UserID Index to TodoSchema

78 |

79 | Create reasonable schemas for your data by following data 80 | modeling best practices. 81 |

82 |

83 | Created Mongoose schemas to support CRUD operations from my 84 | backend 85 |

86 |

(19%) Server

87 |

88 | Create a RESTful API using Node and Express. * For the purposes 89 | of this project, you may forgo the HATEOAS aspect of REST APIs. 90 |

91 |

RESTful API was created using Node and Express

92 |

Include API routes for all four CRUD operations.

93 |

94 | Using my API, I am using all four CRUD operations to 95 | Create/Read/Update/Delete Todos 96 |

97 |

98 | Utilize the native MongoDB driver or Mongoose to interface with 99 | your database. 100 |

101 |

Using mongoose driver to connect API to database

102 |

103 | Include at least one form of user authentication/authorization 104 | within the application. 105 |

106 |

User Authentication is setup using Supabase and PostgreSQL

107 |

(35%) Front-End Development

108 |

Use React to create the application’s front-end.

109 |

110 | My frontend is constructed with Next JS which is a React 111 | Framework 112 |

113 |

Use CSS to style the application.

114 |

I am using CSS modules to style application

115 |

116 | Create at least four different views or pages for the 117 | application. 118 |

119 |

120 | I have created the Home, Dashboard, Signin/Signup, Shop, About, 121 | and Capstone Pages 122 |

123 |

124 | Create some form of navigation that is included across the 125 | application’s pages, utilizing React Router for page rendering. 126 |

127 |

128 | The home page contains a Navbar and the Dashboard contains a 129 | sidebar, responsible for routing. 130 |

131 |

Use React Hooks or Redux for application state management.

132 |

I am using both React Hooks and Redux to Manage State

133 |

134 | Interface directly with the server and API that you created. 135 |

136 |

137 | I am connected directly to my API, deploying from Digital Ocean 138 | App Platform 139 |

140 |

(5%) Presentation

141 |

Create a short overview of your application.

142 | 143 |

144 | Todo List that will be a feature within the Life and Family 145 | management app with a Node Express Api. The web framework will 146 | be managed with the MERN stack combined with Next JS, TypeScript 147 | and Supabase. 148 |

149 | 150 |

Highlight the use cases of your application.

151 |

152 | The Todo List serves to provide a layer of organization in the 153 | grand scheme of the overall Management App. 154 |

155 |

156 | Highlight the technical functionality of the application, from a 157 | high-level perspective. 158 |

159 |

160 | As thoughts of scaling comes into mind, I use scalable tools 161 | such as redux to help manage state, maybe I would opt to use 162 | Docker and Kubernetes if I decided I needed a containerized 163 | infrastructure, which is something I am considering. 164 |

165 |

166 | Discuss what you have learned through the development of the 167 | application. 168 |

169 |

170 | While developing this application, I became better with 171 | typescript all around and picking up Redux, getting it to work 172 | most of the time, has expanded my imagination on what I can 173 | create. Working in the Backend with Express, has made me 174 | realized that I love full-stack and shouldn’t limit myself to 175 | Frontend development. 176 |

177 |

178 | Discuss additional features that could be added to the 179 | application in the future. 180 |

181 |

182 | Some Additional Features that I will be adding to the app will 183 | be more Life and Family management tools, As well as an 184 | Ecommerce platform. For the overall System, I envision it to be 185 | an overall ecosystem for content creators, developers, business 186 | minds, and entrepreneurs. 187 |

188 |

(5%) Extra Credit

189 |

190 | Adhere to Agile principles and the Scrum framework. Perform 191 | stand-up sessions (with an instructor) when possible. 192 |

193 | 194 |

195 | Successfully track your project using a software similar to 196 | Jira. 197 |

198 |

199 | Started tracking project with Trello, However, I already started 200 | tracking in Word. I continued to use my word document. 201 |

202 | 203 | PDF Form of Tracking and Planning 204 | 205 |

Build your application primarily with TypeScript.

206 |

207 | I built my frontend with TypeScript, ran out of time but will be 208 | converting the backend very soon. 209 |

210 |
211 | ); 212 | } 213 | --------------------------------------------------------------------------------