feat: better
This commit is contained in:
		
							parent
							
								
									19afe08bba
								
							
						
					
					
						commit
						38a7e09b68
					
				
					 6 changed files with 1225 additions and 89 deletions
				
			
		|  | @ -21,7 +21,8 @@ | |||
|     "react-dom": "^18.3.1", | ||||
|     "sharp": "^0.33.4", | ||||
|     "tailwind-merge": "^2.3.0", | ||||
|     "tailwindcss-animate": "^1.0.7" | ||||
|     "tailwindcss-animate": "^1.0.7", | ||||
|     "zod": "^3.23.8" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@types/node": "^20.12.12", | ||||
|  |  | |||
|  | @ -44,6 +44,9 @@ dependencies: | |||
|   tailwindcss-animate: | ||||
|     specifier: ^1.0.7 | ||||
|     version: 1.0.7(tailwindcss@3.4.3) | ||||
|   zod: | ||||
|     specifier: ^3.23.8 | ||||
|     version: 3.23.8 | ||||
| 
 | ||||
| devDependencies: | ||||
|   '@types/node': | ||||
|  | @ -3639,3 +3642,7 @@ packages: | |||
|     resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} | ||||
|     engines: {node: '>=10'} | ||||
|     dev: true | ||||
| 
 | ||||
|   /zod@3.23.8: | ||||
|     resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} | ||||
|     dev: false | ||||
|  |  | |||
							
								
								
									
										1141
									
								
								public/projects.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1141
									
								
								public/projects.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										103
									
								
								src/app/page.tsx
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								src/app/page.tsx
									
									
									
									
									
								
							|  | @ -1,85 +1,29 @@ | |||
| 'use client' | ||||
| 
 | ||||
| import React, { useState } from 'react' | ||||
| import { Project } from '@/typings/project' | ||||
| import React, { useState, useEffect } from 'react' | ||||
| import { Project, Tag, projectsSchema } from '@/typings/project' | ||||
| import { ProjectCard } from '@/components/ProjectCard' | ||||
| import projectsData from '../../public/projects.json' | ||||
| 
 | ||||
| const Page = () => { | ||||
|   const projects: Project[] = [ | ||||
|     { | ||||
|       name: 'Project Alpha', | ||||
|       description: 'Description for Project Alpha', | ||||
|       category: 'Web', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { Github: 'https://github.com', YouTube: 'https://youtube.com' }, | ||||
|       languages: ['Java', 'TypeScript'], | ||||
|       tags: ['Frontend', 'Backend'], | ||||
|     }, | ||||
|     { | ||||
|       name: 'Project Beta', | ||||
|       description: 'Description for Project Beta', | ||||
|       category: 'Minecraft', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { Github: 'https://github.com' }, | ||||
|       languages: ['Rust', 'Go'], | ||||
|       tags: ['1.8', 'Fullstack'], | ||||
|     }, | ||||
|     { | ||||
|       name: 'Project Gamma', | ||||
|       description: 'Description for Project Gamma', | ||||
|       category: 'Discord', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { YouTube: 'https://youtube.com' }, | ||||
|       languages: ['TypeScript'], | ||||
|       tags: ['Backend', 'Archived'], | ||||
|     }, | ||||
|     { | ||||
|       name: 'Project Delta', | ||||
|       description: 'Description for Project Delta', | ||||
|       category: 'Unity', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: {}, | ||||
|       languages: ['Java'], | ||||
|       tags: ['1.17', 'Frontend'], | ||||
|     }, | ||||
|     { | ||||
|       name: 'Project Epsilon', | ||||
|       description: 'Description for Project Epsilon', | ||||
|       category: 'Misc', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { Github: 'https://github.com', YouTube: 'https://youtube.com' }, | ||||
|       languages: ['Go', 'Rust'], | ||||
|       tags: ['Fullstack', 'Paper'], | ||||
|     }, | ||||
|     // Add more projects to test pagination
 | ||||
|     { | ||||
|       name: 'Project Zeta', | ||||
|       description: 'Description for Project Zeta', | ||||
|       category: 'Misc', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { Github: 'https://github.com', YouTube: 'https://youtube.com' }, | ||||
|       languages: ['Go', 'Rust'], | ||||
|       tags: ['Fullstack', 'Paper'], | ||||
|     }, | ||||
|     { | ||||
|       name: 'Project Eta', | ||||
|       description: 'Description for Project Eta', | ||||
|       category: 'Misc', | ||||
|       imageUrl: 'https://via.placeholder.com/150', | ||||
|       socials: { Github: 'https://github.com', YouTube: 'https://youtube.com' }, | ||||
|       languages: ['Go', 'Rust'], | ||||
|       tags: ['Fullstack', 'Paper'], | ||||
|     }, | ||||
|     // Add as many projects as needed to test
 | ||||
|   ] | ||||
|   const parsedProjects = projectsSchema.safeParse(projectsData) | ||||
| 
 | ||||
|   if (!parsedProjects.success) { | ||||
|     console.error('Invalid project data', parsedProjects.error) | ||||
|     return <div>Error loading projects</div> | ||||
|   } | ||||
| 
 | ||||
|   const projects: Project[] = parsedProjects.data | ||||
| 
 | ||||
|   const [currentPage, setCurrentPage] = useState(1) | ||||
|   const [itemsPerPage] = useState(4) | ||||
|   const [filterTag, setFilterTag] = useState<string | null>(null) | ||||
|   const [itemsPerPage, setItemsPerPage] = useState<number>(4) | ||||
|   const [filterTag, setFilterTag] = useState<Tag | null>(null) | ||||
|   const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('asc') | ||||
| 
 | ||||
|   const handleFilterChange = (event: React.ChangeEvent<HTMLSelectElement>) => { | ||||
|     setFilterTag(event.target.value === 'All' ? null : event.target.value) | ||||
|     setFilterTag( | ||||
|       event.target.value === 'All' ? null : (event.target.value as Tag) | ||||
|     ) | ||||
|     setCurrentPage(1) // Reset to the first page when filter changes
 | ||||
|   } | ||||
| 
 | ||||
|  | @ -88,6 +32,21 @@ const Page = () => { | |||
|     setCurrentPage(1) // Reset to the first page when sort order changes
 | ||||
|   } | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     const handleResize = () => { | ||||
|       const cardWidth = 240 // Width of a single card (adjust as needed)
 | ||||
|       const windowWidth = window.innerWidth | ||||
|       const newItemsPerPage = Math.floor(windowWidth / cardWidth) | ||||
|       setItemsPerPage(newItemsPerPage > 0 ? newItemsPerPage : 1) | ||||
|     } | ||||
| 
 | ||||
|     handleResize() | ||||
| 
 | ||||
|     window.addEventListener('resize', handleResize) | ||||
| 
 | ||||
|     return () => window.removeEventListener('resize', handleResize) | ||||
|   }, []) | ||||
| 
 | ||||
|   const filteredProjects = filterTag | ||||
|     ? projects.filter(project => project.tags.includes(filterTag)) | ||||
|     : projects | ||||
|  |  | |||
|  | @ -37,7 +37,7 @@ const tagStyles: Record<Tag, string> = { | |||
| 
 | ||||
| export const ProjectCard = ({ project }: Props) => { | ||||
|   return ( | ||||
|     <div className="h-80 w-80 transform overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:-translate-y-2"> | ||||
|     <div className="h-80 w-80 transform overflow-hidden rounded-lg bg-white shadow-md transition-transform hover:scale-105"> | ||||
|       <img | ||||
|         src={project.imageUrl} | ||||
|         alt={project.name} | ||||
|  |  | |||
|  | @ -1,18 +1,46 @@ | |||
| export type Social = 'Github' | 'YouTube' | ||||
| export type Language = 'Java' | 'Rust' | 'Go' | 'TypeScript' | ||||
| import { z } from 'zod' | ||||
| 
 | ||||
| type MinecraftTags = '1.8' | '1.17' | 'Paper' | 'Bungee' | ||||
| type WebTags = 'Frontend' | 'Backend' | 'Fullstack' | ||||
| export type Tag = MinecraftTags | WebTags | 'Archived' | ||||
| export const SocialSchema = z.union([z.literal('Github'), z.literal('YouTube')]) | ||||
| export type Social = z.infer<typeof SocialSchema> | ||||
| 
 | ||||
| export type Category = 'Minecraft' | 'Web' | 'Discord' | 'Misc' | 'Unity' | ||||
| export const LanguageSchema = z.union([ | ||||
|   z.literal('Java'), | ||||
|   z.literal('Rust'), | ||||
|   z.literal('Go'), | ||||
|   z.literal('TypeScript'), | ||||
| ]) | ||||
| export type Language = z.infer<typeof LanguageSchema> | ||||
| 
 | ||||
| export type Project = { | ||||
|   name: string | ||||
|   description: string | ||||
|   category: Category | ||||
|   imageUrl: string | ||||
|   socials: Partial<Record<Social, string>> | ||||
|   languages: Language[] | ||||
|   tags: Tag[] | ||||
| } | ||||
| export const TagSchema = z.union([ | ||||
|   z.literal('1.8'), | ||||
|   z.literal('1.17'), | ||||
|   z.literal('Paper'), | ||||
|   z.literal('Bungee'), | ||||
|   z.literal('Frontend'), | ||||
|   z.literal('Backend'), | ||||
|   z.literal('Fullstack'), | ||||
|   z.literal('Archived'), | ||||
| ]) | ||||
| export type Tag = z.infer<typeof TagSchema> | ||||
| 
 | ||||
| export const CategorySchema = z.union([ | ||||
|   z.literal('Minecraft'), | ||||
|   z.literal('Web'), | ||||
|   z.literal('Discord'), | ||||
|   z.literal('Misc'), | ||||
|   z.literal('Unity'), | ||||
| ]) | ||||
| export type Category = z.infer<typeof CategorySchema> | ||||
| 
 | ||||
| export const ProjectSchema = z.object({ | ||||
|   name: z.string(), | ||||
|   description: z.string(), | ||||
|   category: CategorySchema, | ||||
|   imageUrl: z.string().url(), | ||||
|   socials: z.record(SocialSchema, z.string().url()).optional(), | ||||
|   languages: z.array(LanguageSchema), | ||||
|   tags: z.array(TagSchema), | ||||
| }) | ||||
| export type Project = z.infer<typeof ProjectSchema> | ||||
| 
 | ||||
| export const projectsSchema = z.array(ProjectSchema) | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue