-
Notifications
You must be signed in to change notification settings - Fork 0
/
repository_content.json
35 lines (35 loc) · 38.3 KB
/
repository_content.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
"next.config.mjs": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n reactStrictMode: true,\n images: {\n domains: [\"nextjs.org\"],\n },\n};\n\nexport default nextConfig;\n// /** @type {import('next').NextConfig} */\n// const nextConfig = {};\n\n// export default nextConfig;\n",
"get_structure.py": "import os\nimport json\n\n\ndef get_repository_content(root_path, exclude_list):\n content = {}\n\n for dirpath, dirnames, filenames in os.walk(root_path):\n # Remove excluded directories\n dirnames[:] = [d for d in dirnames if d not in exclude_list]\n\n current_dir = content\n for dir_name in os.path.relpath(dirpath, root_path).split(os.sep):\n if dir_name == \".\":\n continue\n current_dir = current_dir.setdefault(dir_name, {})\n\n for filename in filenames:\n if filename not in exclude_list and not any(\n filename.endswith(ext) for ext in exclude_list\n ):\n file_path = os.path.join(dirpath, filename)\n try:\n with open(file_path, \"r\", encoding=\"utf-8\") as file:\n file_content = file.read()\n current_dir[filename] = file_content\n except Exception as e:\n current_dir[filename] = f\"Error reading file: {str(e)}\"\n\n return content\n\n\n# Exclude list for files and directories\nexclude_list = [\n \".git\",\n \"node_modules\",\n \".next\",\n \".vscode\",\n \".idea\",\n \"__pycache__\",\n \".DS_Store\",\n \"package-lock.json\",\n \".gitignore\",\n \".env\",\n \".env.local\",\n \".jpg\",\n \".jpeg\",\n \".png\",\n \".gif\",\n \".svg\",\n \".ico\",\n \".woff\",\n \".woff2\",\n \".ttf\",\n \".eot\",\n \"repository_content.json\",\n]\n\n# Replace 'path/to/your/repository' with the actual path to your repository\nrepo_path = \"./\"\n\nrepository_content = get_repository_content(repo_path, exclude_list)\n\n# Save the content to a JSON file\nwith open(\"repository_content.json\", \"w\", encoding=\"utf-8\") as json_file:\n json.dump(repository_content, json_file, indent=2)\n\nprint(\"Repository content has been saved to 'repository_content.json'\")\n",
"next-env.d.ts": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.\n",
"README.md": "This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.\n",
"package.json": "{\n \"name\": \"sfusedbooks\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"dev\": \"next dev\",\n \"build\": \"next build\",\n \"start\": \"next start\",\n \"lint\": \"next lint\"\n },\n \"dependencies\": {\n \"@emotion/react\": \"^11.13.3\",\n \"@emotion/styled\": \"^11.13.0\",\n \"@mui/icons-material\": \"^6.1.1\",\n \"@mui/material\": \"^6.1.1\",\n \"@supabase/supabase-js\": \"^2.45.4\",\n \"bcryptjs\": \"^2.4.3\",\n \"jsonwebtoken\": \"^9.0.2\",\n \"jwt-decode\": \"^4.0.0\",\n \"lodash\": \"^4.17.21\",\n \"next\": \"14.2.13\",\n \"react\": \"^18\",\n \"react-dom\": \"^18\",\n \"supabase\": \"^1.192.5\"\n },\n \"devDependencies\": {\n \"@types/lodash\": \"^4.17.7\",\n \"@types/node\": \"^20\",\n \"@types/react\": \"^18\",\n \"@types/react-dom\": \"^18\",\n \"eslint\": \"^8\",\n \"eslint-config-next\": \"14.2.13\",\n \"typescript\": \"^5\"\n }\n}\n",
"tsconfig.json": "{\n \"compilerOptions\": {\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noEmit\": true,\n \"esModuleInterop\": true,\n \"module\": \"esnext\",\n \"moduleResolution\": \"bundler\",\n \"resolveJsonModule\": true,\n \"isolatedModules\": true,\n \"jsx\": \"preserve\",\n \"incremental\": true,\n \"plugins\": [\n {\n \"name\": \"next\"\n }\n ],\n \"paths\": {\n \"@/*\": [\"./*\"]\n }\n },\n \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n \"exclude\": [\"node_modules\"]\n}\n",
".eslintrc.json": "{\n \"extends\": [\"next/core-web-vitals\", \"next/typescript\"],\n \"rules\": {\n \"react-hooks/exhaustive-deps\": \"warn\",\n \"@typescript-eslint/no-unused-vars\": [\"error\", { \"argsIgnorePattern\": \"^_\" }]\n }\n}\n// {\n// \"extends\": [\"next/core-web-vitals\", \"next/typescript\"]\n// }\n",
"app": {
"layout.tsx": "'use client';\n\nimport { ThemeProvider } from '@mui/material/styles';\nimport CssBaseline from '@mui/material/CssBaseline';\nimport theme from '../lib/theme';\nimport { AuthProvider } from '../lib/authContext';\n\nexport default function RootLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n <html lang=\"en\">\n <body>\n <ThemeProvider theme={theme}>\n <CssBaseline />\n <AuthProvider>\n {children}\n </AuthProvider>\n </ThemeProvider>\n </body>\n </html>\n );\n}",
"page.tsx": "// app/page.tsx\n'use client';\n\nimport React, { useState, useEffect } from 'react';\nimport { Container, Typography, TextField, Button, Grid, Card, CardContent, CardActions, InputAdornment, Chip, Box } from '@mui/material';\nimport SearchIcon from '@mui/icons-material/Search';\nimport Navigation from '../components/Navigation';\nimport { supabase } from '../lib/supabaseClient';\n// import Image from 'next/image';\n\ninterface Listing {\n id: number;\n title: string;\n author: string;\n price: number;\n condition: string;\n course_number: string;\n}\n\nexport default function Home() {\n const [listings, setListings] = useState<Listing[]>([]);\n const [search, setSearch] = useState('');\n\n useEffect(() => {\n fetchListings();\n }, []);\n\n const fetchListings = async () => {\n try {\n const { data, error } = await supabase\n .from('listings')\n .select('*')\n .order('created_at', { ascending: false })\n .limit(12);\n\n if (error) {\n throw error;\n } else {\n setListings(data || []);\n }\n } catch (error) {\n console.error('Error fetching listings:', error);\n }\n };\n\n const handleSearch = async () => {\n const { data, error } = await supabase\n .from('listings')\n .select('*')\n .or(`title.ilike.%${search}%,author.ilike.%${search}%,course_number.ilike.%${search}%`)\n .order('created_at', { ascending: false });\n\n if (error) {\n console.error('Error searching listings:', error);\n } else {\n setListings(data || []);\n }\n };\n\n return (\n <>\n <Navigation />\n <Container maxWidth=\"lg\" sx={{ mt: 4 }}>\n <Box sx={{ textAlign: 'center', mb: 4 }}>\n <Typography variant=\"h3\" component=\"h1\" gutterBottom>\n Welcome to SFUsed Books\n </Typography>\n <Typography variant=\"h6\" color=\"text.secondary\" gutterBottom>\n Find and sell textbooks within the SFU community\n </Typography>\n </Box>\n <TextField\n fullWidth\n variant=\"outlined\"\n placeholder=\"Search for textbooks by title, author, or course number...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n sx={{ mb: 2 }}\n InputProps={{\n startAdornment: (\n <InputAdornment position=\"start\">\n <SearchIcon />\n </InputAdornment>\n ),\n endAdornment: (\n <InputAdornment position=\"end\">\n <Button variant=\"contained\" onClick={handleSearch}>\n Search\n </Button>\n </InputAdornment>\n ),\n }}\n />\n <Grid container spacing={3}>\n {listings.map((listing) => (\n <Grid item xs={12} sm={6} md={4} key={listing.id}>\n <Card elevation={3}>\n <CardContent>\n <Typography variant=\"h6\" component=\"div\" noWrap>\n {listing.title}\n </Typography>\n <Typography color=\"text.secondary\" noWrap>\n {listing.author}\n </Typography>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mt: 2 }}>\n <Chip label={listing.condition} color=\"primary\" size=\"small\" />\n <Typography variant=\"h6\" color=\"secondary\">\n ${listing.price.toFixed(2)}\n </Typography>\n </Box>\n {listing.course_number && (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n Course: {listing.course_number}\n </Typography>\n )}\n </CardContent>\n <CardActions>\n <Button size=\"small\" color=\"primary\">View Details</Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n </Container>\n </>\n );\n}",
"dashboard": {
"page.tsx": "// app/dashboard/page.tsx\n'use client';\n\nimport React, { useState, useEffect, useCallback } from 'react';\nimport { Container, Typography, Button, Grid, Card, CardContent, CardActions, Box, Chip, Dialog, DialogTitle, DialogContent } from '@mui/material';\nimport Navigation from '../../components/Navigation';\nimport CreateListing from '../../components/CreateListing';\nimport { useAuth } from '../../lib/authContext';\nimport { useRouter } from 'next/navigation';\nimport { supabase } from '../../lib/supabaseClient';\n\ninterface Listing {\n id: string;\n title: string;\n author: string;\n price: number;\n condition: string;\n course_number: string;\n}\n\nexport default function Dashboard() {\n const [userListings, setUserListings] = useState<Listing[]>([]);\n const [showCreateForm, setShowCreateForm] = useState(false);\n const { user } = useAuth();\n const router = useRouter();\n\n const fetchUserListings = useCallback(async () => {\n try {\n const { data, error } = await supabase\n .from('listings')\n .select('*')\n .eq('user_id', user?.id)\n .order('created_at', { ascending: false });\n\n if (error) throw error;\n setUserListings(data || []);\n } catch (error) {\n console.error('Error fetching user listings:', error);\n }\n}, [user]);\n\n useEffect(() => {\n if (!user) {\n router.push('/login');\n } else {\n fetchUserListings();\n }\n }, [user, router, fetchUserListings]);\n\n\n\n const handleListingCreated = () => {\n setShowCreateForm(false);\n fetchUserListings();\n };\n\n const handleDeleteListing = async (listingId: string) => {\n try {\n const { error } = await supabase\n .from('listings')\n .delete()\n .eq('id', listingId);\n\n if (error) throw error;\n fetchUserListings();\n } catch (error) {\n console.error('Error deleting listing:', error);\n }\n };\n\n if (!user) {\n return null;\n }\n\n return (\n <>\n <Navigation />\n <Container maxWidth=\"lg\" sx={{ mt: 4 }}>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 4 }}>\n <Typography variant=\"h4\" component=\"h1\">\n Your Dashboard\n </Typography>\n <Button\n variant=\"contained\"\n onClick={() => setShowCreateForm(true)}\n >\n Create New Listing\n </Button>\n </Box>\n <Grid container spacing={3}>\n {userListings.map((listing) => (\n <Grid item xs={12} sm={6} md={4} key={listing.id}>\n <Card elevation={3}>\n <CardContent>\n <Typography variant=\"h6\" component=\"div\" noWrap>\n {listing.title}\n </Typography>\n <Typography color=\"text.secondary\" noWrap>\n {listing.author}\n </Typography>\n <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mt: 2 }}>\n <Chip label={listing.condition} color=\"primary\" size=\"small\" />\n <Typography variant=\"h6\" color=\"secondary\">\n ${listing.price.toFixed(2)}\n </Typography>\n </Box>\n {listing.course_number && (\n <Typography variant=\"body2\" sx={{ mt: 1 }}>\n Course: {listing.course_number}\n </Typography>\n )}\n </CardContent>\n <CardActions>\n <Button size=\"small\" color=\"primary\">Edit</Button>\n <Button size=\"small\" color=\"error\" onClick={() => handleDeleteListing(listing.id)}>\n Delete\n </Button>\n </CardActions>\n </Card>\n </Grid>\n ))}\n </Grid>\n </Container>\n <Dialog open={showCreateForm} onClose={() => setShowCreateForm(false)} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Create New Listing</DialogTitle>\n <DialogContent>\n <CreateListing onListingCreated={handleListingCreated} />\n </DialogContent>\n </Dialog>\n </>\n );\n}"
},
"verify-email": {
"page.tsx": "// app/verify-email/page.tsx\n'use client';\n\nimport React, { useEffect, useState, Suspense } from 'react';\nimport { Container, Typography, Alert, CircularProgress } from '@mui/material';\nimport { useRouter, useSearchParams } from 'next/navigation';\nimport { supabase } from '../../lib/supabaseClient';\n\nfunction VerifyEmailContent() {\n const [verifying, setVerifying] = useState(true);\n const [error, setError] = useState('');\n const router = useRouter();\n const searchParams = useSearchParams();\n\n useEffect(() => {\n const verifyEmail = async () => {\n const token = searchParams.get('token');\n const type = searchParams.get('type');\n\n if (type !== 'signup' || !token) {\n setError('Invalid verification link');\n setVerifying(false);\n return;\n }\n\n try {\n const { error } = await supabase.auth.verifyOtp({\n token,\n type: 'signup',\n email: '',\n });\n if (error) throw error;\n \n // Redirect to login page after successful verification\n router.push('/login');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'An error occurred');\n setVerifying(false);\n }\n };\n\n verifyEmail();\n }, [router, searchParams]);\n\n if (verifying) {\n return (\n <Container maxWidth=\"sm\" sx={{ mt: 4, textAlign: 'center' }}>\n <CircularProgress />\n <Typography variant=\"h6\" sx={{ mt: 2 }}>\n Verifying your email...\n </Typography>\n </Container>\n );\n }\n\n return (\n <Container maxWidth=\"sm\" sx={{ mt: 4 }}>\n <Typography variant=\"h4\" component=\"h1\" gutterBottom>\n Email Verification\n </Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n {!error && (\n <Alert severity=\"success\">\n Your email has been verified. You can now log in.\n </Alert>\n )}\n </Container>\n );\n}\n\nexport default function VerifyEmail() {\n return (\n <Suspense fallback={<CircularProgress />}>\n <VerifyEmailContent />\n </Suspense>\n );\n}"
},
"register": {
"page.tsx": "// app/register/page.tsx\n'use client';\n\nimport React, { useState } from 'react';\nimport { Container, Typography, TextField, Button, Box, Alert } from '@mui/material';\nimport Navigation from '../../components/Navigation';\nimport { useAuth } from '../../lib/authContext';\nimport { useRouter } from 'next/navigation';\nimport { debounce } from 'lodash';\n\nfunction getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\nexport default function Register() {\n const [name, setName] = useState('');\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [error, setError] = useState('');\n const [success, setSuccess] = useState('');\n const { signUp } = useAuth();\n const router = useRouter();\n\n const debouncedSignUp = debounce(async (email, password, name) => {\n try {\n await signUp(email, password, name);\n setSuccess('Registration successful. Please check your email to confirm your account.');\n setTimeout(() => router.push('/login'), 5000);\n } catch (err) {\n const errorMessage = getErrorMessage(err);\n if (errorMessage.includes('Too many requests')) {\n setError('Too many signup attempts. Please try again later.');\n } else {\n setError(errorMessage);\n }\n }\n }, 1000);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n setError('');\n setSuccess('');\n debouncedSignUp(email, password, name);\n };\n\n return (\n <>\n <Navigation />\n <Container maxWidth=\"sm\" sx={{ mt: 4 }}>\n <Typography variant=\"h4\" component=\"h1\" gutterBottom>\n Register\n </Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n {success && <Alert severity=\"success\">{success}</Alert>}\n <Box component=\"form\" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"name\"\n label=\"Full Name\"\n name=\"name\"\n autoComplete=\"name\"\n autoFocus\n value={name}\n onChange={(e) => setName(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"email\"\n label=\"Email Address\"\n name=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n required\n fullWidth\n name=\"password\"\n label=\"Password\"\n type=\"password\"\n id=\"password\"\n autoComplete=\"new-password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n />\n <Button\n type=\"submit\"\n fullWidth\n variant=\"contained\"\n sx={{ mt: 3, mb: 2 }}\n >\n Register\n </Button>\n </Box>\n </Container>\n </>\n );\n}\n// 'use client';\n\n// import React, { useState } from 'react';\n// import { Container, Typography, TextField, Button, Box, Alert } from '@mui/material';\n// import Navigation from '../../components/Navigation';\n// import { useAuth } from '../../lib/authContext';\n// import { useRouter } from 'next/navigation';\n// import { debounce } from 'lodash'; // Make sure to install lodash: npm install lodash\n\n// // import { getErrorMessage } from '../../utils/errorHandler'; // Import the function\n\n// function getErrorMessage(error: unknown): string {\n// if (error instanceof Error) return error.message;\n// return String(error);\n// }\n\n// export default function Register() {\n// const [name, setName] = useState('');\n// const [email, setEmail] = useState('');\n// const [password, setPassword] = useState('');\n// const [studentNumber, setStudentNumber] = useState('');\n// const [error, setError] = useState('');\n// const [success, setSuccess] = useState('');\n// const { signUp } = useAuth();\n// const router = useRouter();\n// const debouncedSignUp = debounce(async (email, password, name, studentNumber) => {\n// try {\n// await signUp(email, password, name, studentNumber);\n// setSuccess('Registration successful. Please check your email to confirm your account.');\n// setTimeout(() => router.push('/login'), 5000);\n// } catch (err) {\n// const errorMessage = getErrorMessage(err);\n// if (errorMessage.includes('Too many requests')) {\n// setError('Too many signup attempts. Please try again later.');\n// } else {\n// setError(errorMessage);\n// }\n// }\n// }, 1000);\n// const handleSubmit = (e: React.FormEvent) => {\n// e.preventDefault();\n// setError('');\n// setSuccess('');\n// debouncedSignUp(email, password, name, studentNumber);\n// };\n// return (\n// <>\n// <Navigation />\n// <Container maxWidth=\"sm\" sx={{ mt: 4 }}>\n// <Typography variant=\"h4\" component=\"h1\" gutterBottom>\n// Register\n// </Typography>\n// {error && <Alert severity=\"error\">{error}</Alert>}\n// {success && <Alert severity=\"success\">{success}</Alert>}\n// <Box component=\"form\" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>\n// <TextField\n// margin=\"normal\"\n// required\n// fullWidth\n// id=\"name\"\n// label=\"Full Name\"\n// name=\"name\"\n// autoComplete=\"name\"\n// autoFocus\n// value={name}\n// onChange={(e) => setName(e.target.value)}\n// />\n// <TextField\n// margin=\"normal\"\n// required\n// fullWidth\n// id=\"email\"\n// label=\"Email Address\"\n// name=\"email\"\n// autoComplete=\"email\"\n// value={email}\n// onChange={(e) => setEmail(e.target.value)}\n// />\n// <TextField\n// margin=\"normal\"\n// required\n// fullWidth\n// name=\"password\"\n// label=\"Password\"\n// type=\"password\"\n// id=\"password\"\n// autoComplete=\"new-password\"\n// value={password}\n// onChange={(e) => setPassword(e.target.value)}\n// />\n// <TextField\n// margin=\"normal\"\n// required\n// fullWidth\n// id=\"studentNumber\"\n// label=\"Student Number\"\n// name=\"studentNumber\"\n// value={studentNumber}\n// onChange={(e) => setStudentNumber(e.target.value)}\n// />\n// <Button\n// type=\"submit\"\n// fullWidth\n// variant=\"contained\"\n// sx={{ mt: 3, mb: 2 }}\n// >\n// Register\n// </Button>\n// </Box>\n// </Container>\n// </>\n// );\n// }"
},
"api": {},
"login": {
"page.tsx": "'use client';\n\nimport React, { useState } from 'react';\nimport { Container, Typography, TextField, Button, Box, Alert } from '@mui/material';\nimport Navigation from '../../components/Navigation';\nimport { useAuth } from '../../lib/authContext';\nimport { useRouter } from 'next/navigation';\n\nexport default function Login() {\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [error, setError] = useState('');\n const { signIn } = useAuth();\n const router = useRouter();\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError('');\n \n try {\n await signIn(email, password);\n router.push('/dashboard');\n } catch (err) {\n if (err instanceof Error) {\n setError(err.message);\n } else {\n setError('An unexpected error occurred');\n }\n }\n };\n\n return (\n <>\n <Navigation />\n <Container maxWidth=\"sm\" sx={{ mt: 4 }}>\n <Typography variant=\"h4\" component=\"h1\" gutterBottom>\n Login\n </Typography>\n {error && <Alert severity=\"error\">{error}</Alert>}\n <Box component=\"form\" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"email\"\n label=\"Email Address\"\n name=\"email\"\n autoComplete=\"email\"\n autoFocus\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n required\n fullWidth\n name=\"password\"\n label=\"Password\"\n type=\"password\"\n id=\"password\"\n autoComplete=\"current-password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n />\n <Button\n type=\"submit\"\n fullWidth\n variant=\"contained\"\n sx={{ mt: 3, mb: 2 }}\n >\n Sign In\n </Button>\n </Box>\n </Container>\n </>\n );\n}"
}
},
"components": {
"CreateListing.tsx": "// components/CreateListing.tsx\nimport React, { useState } from \"react\";\nimport { TextField, Button, Box, Typography, Alert, MenuItem } from \"@mui/material\";\nimport { useAuth } from '../lib/authContext';\nimport { supabase } from '../lib/supabaseClient';\n\n\n\nexport function getErrorMessage(error: unknown): string {\n if (error instanceof Error) return error.message;\n return String(error);\n}\n\ninterface CreateListingProps {\n onListingCreated: () => void;\n}\n\n\nconst conditions = [\n \"New\",\n \"Like New\",\n \"Very Good\",\n \"Good\",\n \"Acceptable\",\n];\n\nconst CreateListing: React.FC<CreateListingProps> = ({ onListingCreated }) => {\n const { user } = useAuth();\n\n const [title, setTitle] = useState(\"\");\n const [author, setAuthor] = useState(\"\");\n const [edition, setEdition] = useState(\"\");\n const [condition, setCondition] = useState(\"\");\n const [price, setPrice] = useState(\"\");\n const [courseNumber, setCourseNumber] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [error, setError] = useState(\"\");\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setError(\"\");\n\n if (!user) {\n setError(\"You must be logged in to create a listing\");\n return;\n }\n\n try {\n const { error } = await supabase\n .from('listings')\n .insert({\n user_id: user.id,\n title,\n author,\n edition,\n condition,\n price: parseFloat(price),\n course_number: courseNumber,\n description,\n });\n\n if (error) throw error;\n\n onListingCreated();\n // Reset form fields\n setTitle(\"\");\n setAuthor(\"\");\n setEdition(\"\");\n setCondition(\"\");\n setPrice(\"\");\n setCourseNumber(\"\");\n setDescription(\"\");\n } catch (err) {\n // setError(err.message);\n setError(getErrorMessage(err));\n }\n };\n\n return (\n <Box component=\"form\" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>\n {error && <Alert severity=\"error\" sx={{ mb: 2 }}>{error}</Alert>}\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"title\"\n label=\"Book Title\"\n name=\"title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"author\"\n label=\"Author\"\n name=\"author\"\n value={author}\n onChange={(e) => setAuthor(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n fullWidth\n id=\"edition\"\n label=\"Edition\"\n name=\"edition\"\n value={edition}\n onChange={(e) => setEdition(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"condition\"\n select\n label=\"Condition\"\n name=\"condition\"\n value={condition}\n onChange={(e) => setCondition(e.target.value)}\n >\n {conditions.map((option) => (\n <MenuItem key={option} value={option}>\n {option}\n </MenuItem>\n ))}\n </TextField>\n <TextField\n margin=\"normal\"\n required\n fullWidth\n id=\"price\"\n label=\"Price\"\n name=\"price\"\n type=\"number\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n InputProps={{\n startAdornment: <Typography>$</Typography>,\n }}\n />\n <TextField\n margin=\"normal\"\n fullWidth\n id=\"courseNumber\"\n label=\"Course Number\"\n name=\"courseNumber\"\n value={courseNumber}\n onChange={(e) => setCourseNumber(e.target.value)}\n />\n <TextField\n margin=\"normal\"\n fullWidth\n id=\"description\"\n label=\"Description\"\n name=\"description\"\n multiline\n rows={4}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n <Button type=\"submit\" fullWidth variant=\"contained\" sx={{ mt: 3, mb: 2 }}>\n Create Listing\n </Button>\n </Box>\n );\n};\n\nexport default CreateListing;",
"Navigation.tsx": "/* eslint-disable @typescript-eslint/no-unused-vars */\n// components/Navigation.tsx\nimport React from 'react';\nimport AppBar from '@mui/material/AppBar';\nimport Toolbar from '@mui/material/Toolbar';\nimport Typography from '@mui/material/Typography';\nimport Button from '@mui/material/Button';\nimport Link from 'next/link';\nimport { useAuth } from '../lib/authContext';\nimport { Box, Menu, MenuItem, IconButton, Avatar } from '@mui/material';\n// import { AccountCircle } from '@mui/icons-material';\nimport { styled } from '@mui/material/styles';\n\nconst StyledAppBar = styled(AppBar)(({ theme }) => ({\n background: 'linear-gradient(45deg, #8C1D40 30%, #C1A01E 90%)',\n boxShadow: '0 3px 5px 2px rgba(140, 29, 64, .3)',\n}));\n\nconst StyledButton = styled(Button)(({ theme }) => ({\n borderRadius: '20px',\n padding: '6px 16px',\n margin: '0 8px',\n color: 'white',\n fontWeight: 'bold',\n textTransform: 'none',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.1)',\n },\n}));\n\nconst StyledIconButton = styled(IconButton)(({ theme }) => ({\n color: 'white',\n '&:hover': {\n backgroundColor: 'rgba(255, 255, 255, 0.1)',\n },\n}));\n\nconst Navigation = () => {\n const { user, signOut } = useAuth();\n const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);\n\n const handleMenu = (event: React.MouseEvent<HTMLElement>) => {\n setAnchorEl(event.currentTarget);\n };\n\n const handleClose = () => {\n setAnchorEl(null);\n };\n\n return (\n <StyledAppBar position=\"static\">\n <Toolbar>\n <Typography variant=\"h6\" component=\"div\" sx={{ flexGrow: 1, fontWeight: 'bold' }}>\n SFUsed Books\n </Typography>\n <Box sx={{ display: 'flex', alignItems: 'center' }}>\n <Link href=\"/\" passHref>\n <StyledButton>Home</StyledButton>\n </Link>\n {user ? (\n <>\n <Link href=\"/dashboard\" passHref>\n <StyledButton>Dashboard</StyledButton>\n </Link>\n <StyledIconButton\n size=\"large\"\n aria-label=\"account of current user\"\n aria-controls=\"menu-appbar\"\n aria-haspopup=\"true\"\n onClick={handleMenu}\n >\n <Avatar sx={{ width: 32, height: 32, bgcolor: 'secondary.main' }}>\n {user.email?.[0].toUpperCase()}\n </Avatar>\n </StyledIconButton>\n <Menu\n id=\"menu-appbar\"\n anchorEl={anchorEl}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'right',\n }}\n keepMounted\n transformOrigin={{\n vertical: 'top',\n horizontal: 'right',\n }}\n open={Boolean(anchorEl)}\n onClose={handleClose}\n >\n <MenuItem onClick={handleClose}>Profile</MenuItem>\n <MenuItem onClick={signOut}>Logout</MenuItem>\n </Menu>\n </>\n ) : (\n <>\n <Link href=\"/login\" passHref>\n <StyledButton>Login</StyledButton>\n </Link>\n <Link href=\"/register\" passHref>\n <StyledButton variant=\"outlined\" sx={{ borderColor: 'white', '&:hover': { borderColor: 'white' } }}>\n Register\n </StyledButton>\n </Link>\n </>\n )}\n </Box>\n </Toolbar>\n </StyledAppBar>\n );\n};\n\nexport default Navigation;"
},
"lib": {
"authContext.tsx": "// lib/authContext.tsx\nimport React, { createContext, useContext, useState, useEffect } from 'react';\nimport { User } from '@supabase/supabase-js';\nimport { supabase } from './supabaseClient';\n\ninterface AuthContextType {\n user: User | null;\n loading: boolean;\n signUp: (email: string, password: string, name: string) => Promise<void>;\n signIn: (email: string, password: string) => Promise<void>;\n signOut: () => Promise<void>;\n}\n\nconst AuthContext = createContext<AuthContextType>({\n user: null,\n loading: true,\n signUp: async () => {},\n signIn: async () => {},\n signOut: async () => {},\n});\n\nexport const useAuth = () => useContext(AuthContext);\n\nexport const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\n const [user, setUser] = useState<User | null>(null);\n const [loading, setLoading] = useState(true);\n\n useEffect(() => {\n const fetchSession = async () => {\n try {\n const { data: { session } } = await supabase.auth.getSession();\n setUser(session?.user ?? null);\n } catch (error) {\n console.error('Error fetching session:', error);\n } finally {\n setLoading(false);\n }\n };\n \n fetchSession();\n \n const { data: listener } = supabase.auth.onAuthStateChange((_event, session) => {\n setUser(session?.user ?? null);\n setLoading(false);\n });\n \n return () => {\n listener?.subscription.unsubscribe();\n };\n }, []);\n\n const signUp = async (email: string, password: string, name: string) => {\n const { error } = await supabase.auth.signUp({\n email,\n password,\n options: {\n data: {\n name,\n },\n },\n });\n if (error) throw error;\n };\n\n const signIn = async (email: string, password: string) => {\n const { error } = await supabase.auth.signInWithPassword({ email, password });\n if (error) throw error;\n };\n\n const signOut = async () => {\n const { error } = await supabase.auth.signOut();\n if (error) throw error;\n };\n\n return (\n <AuthContext.Provider value={{ user, loading, signUp, signIn, signOut }}>\n {children}\n </AuthContext.Provider>\n );\n};",
"supabaseClient.js": "// lib/supabaseClient.js\nimport { createClient } from \"@supabase/supabase-js\";\n\n// const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;\n// const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;\nconst supabaseUrl = \"https://xezsdlmhqejjkfecmiqt.supabase.co\";\nconst supabaseServiceRoleKey =\n \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhlenNkbG1ocWVqamtmZWNtaXF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MjcwNTA4MjMsImV4cCI6MjA0MjYyNjgyM30.oTD0E4KEMHK1DOuAky16P4rmjde_TWQm-VQaA2VvQ9U\";\n// NEXT_PUBLIC_SUPABASE_URL=https://xezsdlmhqejjkfecmiqt.supabase.co\n// NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhlenNkbG1ocWVqamtmZWNtaXF0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MjcwNTA4MjMsImV4cCI6MjA0MjYyNjgyM30.oTD0E4KEMHK1DOuAky16P4rmjde_TWQm-VQaA2VvQ9U\nexport const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);\n",
"theme.ts": "// lib/theme.ts\n'use client';\n\nimport { createTheme } from '@mui/material/styles';\n\nconst theme = createTheme({\n palette: {\n primary: {\n main: '#8C1D40', // SFU Red\n },\n secondary: {\n main: '#C1A01E', // SFU Gold\n },\n },\n typography: {\n fontFamily: 'Arial, sans-serif',\n },\n components: {\n MuiButton: {\n styleOverrides: {\n root: {\n borderRadius: '20px',\n textTransform: 'none',\n },\n },\n },\n MuiAppBar: {\n styleOverrides: {\n root: {\n boxShadow: '0 3px 5px 2px rgba(140, 29, 64, .3)',\n },\n },\n },\n },\n});\n\nexport default theme;"
}
}