Sifa professional network frontend (Next.js, React, TailwindCSS) sifa.id/
at main 61 lines 2.3 kB view raw
1'use client'; 2 3import { useTranslations } from 'next-intl'; 4import { TimelineSection, TimelineEntry, formatTimelineDate } from './timeline'; 5import { EditableSection, EditableEntry, PROJECT_FIELDS } from '@/components/profile-editor'; 6import { projectToValues, valuesToProject } from '@/components/profile-editor/section-converters'; 7import { sortByDateDesc, dateRangeExtractor } from '@/lib/sort-by-date'; 8import type { ProfileProject } from '@/lib/types'; 9 10interface ProjectsSectionProps { 11 projects: ProfileProject[]; 12 isOwnProfile?: boolean; 13} 14 15export function ProjectsSection({ projects, isOwnProfile }: ProjectsSectionProps) { 16 const t = useTranslations('sections'); 17 18 if (!projects.length && !isOwnProfile) return null; 19 20 return ( 21 <TimelineSection title={t('projects')} itemCount={projects.length}> 22 <EditableSection<ProfileProject> 23 sectionTitle={t('projects')} 24 profileKey="projects" 25 isOwnProfile={isOwnProfile} 26 fields={PROJECT_FIELDS} 27 toValues={projectToValues} 28 fromValues={ 29 valuesToProject as (v: Record<string, string | boolean>) => Omit<ProfileProject, 'rkey'> 30 } 31 collection="id.sifa.profile.project" 32 maxVisible={3} 33 sortItems={(items) => sortByDateDesc(items, dateRangeExtractor)} 34 renderEntry={(proj, controls) => ( 35 <EditableEntry 36 key={proj.rkey} 37 isOwnProfile={isOwnProfile} 38 onEdit={controls?.onEdit ?? (() => {})} 39 onDelete={controls?.onDelete ?? (() => {})} 40 entryLabel={proj.name} 41 > 42 <TimelineEntry 43 title={proj.name} 44 subtitle={proj.url ? proj.url.replace(/^https?:\/\/(www\.)?/, '') : ''} 45 dateRange={formatProjectDateRange(proj.startDate, proj.endDate)} 46 description={proj.description} 47 isLast={false} 48 /> 49 </EditableEntry> 50 )} 51 /> 52 </TimelineSection> 53 ); 54} 55 56function formatProjectDateRange(start?: string, end?: string): string { 57 if (!start && !end) return ''; 58 if (!start) return end ? formatTimelineDate(end) : ''; 59 if (!end) return formatTimelineDate(start); 60 return `${formatTimelineDate(start)} - ${formatTimelineDate(end)}`; 61}