first commit

This commit is contained in:
Sylvain Duchesne
2026-01-18 11:53:42 +01:00
commit f04f15d926
112 changed files with 24858 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
import React from 'react';
import { Header, Text, Input, Button, Placeholder, Divider } from '../components/sketchy';
import type { ScreenProps } from './index';
export function CreateEventScreen({ navigate }: ScreenProps) {
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Créer un événement"
left={<span onClick={() => navigate('home')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, padding: 16, overflow: 'auto' }}>
{/* Cover image upload */}
<Placeholder
height={140}
label="+ Ajouter une photo"
style={{ marginBottom: 20, cursor: 'pointer' }}
/>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Nom de l'événement *</Text>
<Input placeholder="Donnez un nom à votre événement" />
</div>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Date *</Text>
<Input type="date" placeholder="Sélectionner une date" />
</div>
<div style={{ display: 'flex', gap: 12 }}>
<div style={{ flex: 1 }}>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Heure de début *</Text>
<Input type="time" placeholder="Début" />
</div>
<div style={{ flex: 1 }}>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Heure de fin</Text>
<Input type="time" placeholder="Fin" />
</div>
</div>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Lieu *</Text>
<Input placeholder="Ajouter un lieu" />
</div>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Description</Text>
<textarea
className="sketchy-input"
placeholder="Décrivez votre événement..."
rows={4}
style={{ resize: 'none' }}
/>
</div>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Thématique *</Text>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
{[
{ id: 'culture', label: 'Culture', emoji: '🎭' },
{ id: 'sport', label: 'Sport', emoji: '' },
{ id: 'nature', label: 'Nature', emoji: '🌿' },
{ id: 'social', label: 'Social', emoji: '👥' },
{ id: 'food', label: 'Gastronomie', emoji: '🍽' },
{ id: 'music', label: 'Musique', emoji: '🎵' },
{ id: 'tech', label: 'Tech', emoji: '💻' },
{ id: 'other', label: 'Autre', emoji: '' },
].map((theme) => (
<Button
key={theme.id}
variant={theme.id === 'social' ? 'primary' : 'default'}
style={{ fontSize: 13 }}
>
{theme.emoji} {theme.label}
</Button>
))}
</div>
</div>
<Divider />
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Qui peut voir cet événement ?</Text>
<div style={{ display: 'flex', gap: 8 }}>
<Button style={{ flex: 1 }}>Public</Button>
<Button variant="primary" style={{ flex: 1 }}>Amis</Button>
<Button style={{ flex: 1 }}>Sur invitation</Button>
</div>
</div>
</div>
</div>
{/* Footer */}
<div style={{ padding: 16, borderTop: '2px solid var(--sketch-black)' }}>
<Button
variant="primary"
style={{ width: '100%' }}
onClick={() => navigate('event-detail')}
>
Créer l'événement
</Button>
</div>
</div>
);
}
+130
View File
@@ -0,0 +1,130 @@
import React, { useState } from 'react';
import { Header, Title, Text, Button, Avatar, Placeholder, Divider } from '../components/sketchy';
import type { ScreenProps } from './index';
export function EventDetailScreen({ navigate }: ScreenProps) {
const [isJoined, setIsJoined] = useState(false);
const attendees = [
{ initials: 'MD', name: 'Marie' },
{ initials: 'PD', name: 'Pierre' },
{ initials: 'SL', name: 'Sophie' },
{ initials: 'TM', name: 'Thomas' },
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Événement"
left={<span onClick={() => navigate('events')} style={{ cursor: 'pointer' }}></span>}
right={<span style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto' }}>
{/* Cover image */}
<Placeholder height={180} label="Photo de couverture" />
<div style={{ padding: 16 }}>
<Title className="user-content" style={{ marginBottom: 8 }}>Barbecue d'été</Title>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, marginBottom: 16 }}>
<Text style={{ margin: 0, fontSize: 15 }}>
📅 <span className="user-content">Samedi 25 janvier 2025</span>
</Text>
<Text style={{ margin: 0, fontSize: 15 }}>
🕓 <span className="user-content">16h00 - 21h00</span>
</Text>
<Text style={{ margin: 0, fontSize: 15 }}>
📍 <span className="user-content">Parc Central, Pelouse Ouest</span>
</Text>
</div>
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
<Button
variant={isJoined ? 'default' : 'primary'}
onClick={() => setIsJoined(!isJoined)}
style={{ flex: 1 }}
>
{isJoined ? ' Inscrit' : 'Participer'}
</Button>
<Button onClick={() => navigate('invite')}>Inviter</Button>
</div>
{isJoined && (
<Button
onClick={() => navigate('meeting-points')}
style={{ width: '100%', marginBottom: 16 }}
>
📍 Points de rencontre
</Button>
)}
<Divider />
{/* Host */}
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16 }}>
<Avatar initials="MD" />
<div>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>Marie Dupont</Text>
<Text style={{ margin: 0, fontSize: 14, color: 'var(--sketch-gray)' }}>Organisateur</Text>
</div>
</div>
<Divider />
{/* Description */}
<Text style={{ fontWeight: 'bold', marginBottom: 8 }}>À propos</Text>
<Text className="user-content" style={{ lineHeight: 1.6 }}>
Rejoignez-nous pour un super barbecue d'é ! Au menu : burgers, saucisses, options végé
et plein de boissons. Apportez votre plat préféré à partager. Jeux et musique assurés !
</Text>
<Divider />
{/* Attendees */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12 }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>Participants (12)</Text>
<Text
style={{ margin: 0, fontSize: 14, cursor: 'pointer' }}
onClick={() => navigate('participants-list')}
>
Voir tout
</Text>
</div>
<div style={{ display: 'flex', gap: 12 }}>
{attendees.map((a, i) => (
<div
key={i}
style={{ textAlign: 'center', cursor: 'pointer' }}
onClick={() => navigate('user-profile')}
>
<Avatar initials={a.initials} size="sm" />
<Text className="user-content" style={{ margin: '4px 0 0 0', fontSize: 12 }}>{a.name}</Text>
</div>
))}
<div
style={{ textAlign: 'center', cursor: 'pointer' }}
onClick={() => navigate('participants-list')}
>
<div style={{
width: 32,
height: 32,
borderRadius: '50%',
background: 'var(--sketch-light-gray)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 12,
}}>
+8
</div>
<Text style={{ margin: '4px 0 0 0', fontSize: 12 }}>autres</Text>
</div>
</div>
</div>
</div>
</div>
);
}
+102
View File
@@ -0,0 +1,102 @@
import React from 'react';
import { Header, Input, Card, Text, Badge, NavBar } from '../components/sketchy';
import type { ScreenProps } from './index';
function EventCard({ title, date, location, attendees, onClick }: {
title: string;
date: string;
location: string;
attendees: number;
onClick: () => void;
}) {
return (
<Card onClick={onClick} style={{ marginBottom: 12 }}>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{title}</Text>
<Text style={{ margin: '4px 0', fontSize: 14 }}>
📅 <span className="user-content">{date}</span>
</Text>
<Text style={{ margin: '0 0 8px 0', fontSize: 14 }}>
📍 <span className="user-content">{location}</span>
</Text>
<Badge>{attendees} inscrits</Badge>
</Card>
);
}
export function EventsScreen({ navigate }: ScreenProps) {
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Découvrir"
left={<span onClick={() => navigate('home')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Search */}
<div style={{ padding: '12px 16px', borderBottom: '1px solid var(--sketch-light-gray)' }}>
<Input placeholder="Rechercher un événement..." />
</div>
{/* Filter tabs */}
<div style={{
display: 'flex',
gap: 8,
padding: '12px 16px',
borderBottom: '1px solid var(--sketch-light-gray)',
}}>
<Badge style={{ background: 'var(--sketch-black)', color: 'var(--sketch-white)' }}>Tous</Badge>
<Badge>Cette semaine</Badge>
<Badge>Proches</Badge>
<Badge>Amis</Badge>
</div>
{/* Content */}
<div style={{ flex: 1, padding: 16, overflow: 'auto' }}>
<EventCard
title="Barbecue d'été"
date="Sam. 25 jan. · 16h00"
location="Parc Central"
attendees={12}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Soirée jeux de société"
date="Ven. 31 jan. · 19h00"
location="Chez Joe"
attendees={8}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Randonnée"
date="Dim. 2 fév. · 9h00"
location="Sentier de montagne"
attendees={5}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Marathon films"
date="Sam. 8 fév. · 18h00"
location="Chez Sarah"
attendees={6}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Yoga au parc"
date="Dim. 9 fév. · 8h00"
location="Parc Riverside"
attendees={15}
onClick={() => navigate('event-detail')}
/>
</div>
{/* Bottom Nav */}
<NavBar
items={[
{ icon: '⌂', label: 'Accueil', onClick: () => navigate('home') },
{ icon: '◎', label: 'Découvrir', active: true },
{ icon: '+', label: 'Créer', onClick: () => navigate('create-event') },
{ icon: '☺', label: 'Profil', onClick: () => navigate('profile') },
]}
/>
</div>
);
}
+117
View File
@@ -0,0 +1,117 @@
import React, { useState } from 'react';
import { Header, Text, Avatar, Input, Button, Badge } from '../components/sketchy';
import type { ScreenProps } from './index';
export function FriendsListScreen({ navigate }: ScreenProps) {
const [activeTab, setActiveTab] = useState<'friends' | 'public'>('friends');
const friends = [
{ initials: 'JD', name: 'Jean Durand', username: '@jeandurand', events: 5, mutual: true },
{ initials: 'AM', name: 'Alice Martin', username: '@alice', events: 12, mutual: true },
{ initials: 'BM', name: 'Baptiste Morel', username: '@baptiste', events: 3, mutual: true },
{ initials: 'CD', name: 'Camille Dubois', username: '@camille', events: 8, mutual: true },
{ initials: 'DL', name: 'David Leroy', username: '@david', events: 2, mutual: true },
{ initials: 'EG', name: 'Emma Girard', username: '@emma', events: 7, mutual: true },
];
const publicProfiles = [
{ initials: 'LB', name: 'Léa Bernard', username: '@leabernard', events: 45, role: 'Organisatrice' },
{ initials: 'MR', name: 'Marc Richard', username: '@marcrichard', events: 67, role: 'Animateur' },
{ initials: 'SF', name: 'Sophie Fontaine', username: '@sophief', events: 23, role: 'Créatrice' },
{ initials: 'PG', name: 'Pierre Gagnon', username: '@pierreg', events: 89, role: 'Organisateur' },
];
const displayedList = activeTab === 'friends' ? friends : publicProfiles;
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Mon réseau"
left={<span onClick={() => navigate('profile')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Tabs */}
<div style={{ display: 'flex', borderBottom: '2px solid var(--sketch-black)' }}>
<button
onClick={() => setActiveTab('friends')}
style={{
flex: 1,
padding: '12px 16px',
background: activeTab === 'friends' ? 'var(--sketch-light-gray)' : 'transparent',
border: 'none',
borderBottom: activeTab === 'friends' ? '3px solid var(--sketch-black)' : '3px solid transparent',
fontFamily: 'var(--font-sketch)',
fontSize: 14,
fontWeight: activeTab === 'friends' ? 'bold' : 'normal',
cursor: 'pointer',
}}
>
Mes amis ({friends.length})
</button>
<button
onClick={() => setActiveTab('public')}
style={{
flex: 1,
padding: '12px 16px',
background: activeTab === 'public' ? 'var(--sketch-light-gray)' : 'transparent',
border: 'none',
borderBottom: activeTab === 'public' ? '3px solid var(--sketch-black)' : '3px solid transparent',
fontFamily: 'var(--font-sketch)',
fontSize: 14,
fontWeight: activeTab === 'public' ? 'bold' : 'normal',
cursor: 'pointer',
}}
>
Profils publics
</button>
</div>
{/* Search bar */}
<div style={{ padding: 16, borderBottom: '1px solid var(--sketch-light-gray)' }}>
<Input placeholder="Rechercher..." />
</div>
{/* List */}
<div style={{ flex: 1, overflow: 'auto' }}>
{displayedList.map((person, i) => (
<div
key={i}
onClick={() => navigate('user-profile')}
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
padding: '12px 16px',
cursor: 'pointer',
borderBottom: '1px solid var(--sketch-light-gray)',
}}
>
<Avatar initials={person.initials} size="sm" />
<div style={{ flex: 1 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{person.name}</Text>
{'role' in person && (
<Badge>{person.role}</Badge>
)}
</div>
<Text style={{ margin: 0, fontSize: 13 }}>
<span className="user-content">{person.username}</span>
<span style={{ color: 'var(--sketch-gray)' }}> · {person.events} événements</span>
</Text>
</div>
<Text style={{ margin: 0, fontSize: 20, color: 'var(--sketch-gray)' }}></Text>
</div>
))}
</div>
{/* Add friend button */}
{activeTab === 'friends' && (
<div style={{ padding: 16, borderTop: '2px solid var(--sketch-black)' }}>
<Button variant="primary" style={{ width: '100%' }}>
+ Ajouter un ami
</Button>
</div>
)}
</div>
);
}
+79
View File
@@ -0,0 +1,79 @@
import React from 'react';
import { Button, Title, Text, Card, NavBar, Badge } from '../components/sketchy';
import type { ScreenProps } from './index';
function EventCard({ title, date, attendees, onClick }: { title: string; date: string; attendees: number; onClick: () => void }) {
return (
<Card onClick={onClick} style={{ marginBottom: 12 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{title}</Text>
<Text className="user-content" style={{ margin: '4px 0 0 0', fontSize: 14 }}>{date}</Text>
</div>
<Badge>{attendees} inscrits</Badge>
</div>
</Card>
);
}
export function HomeScreen({ navigate }: ScreenProps) {
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{/* Header */}
<div style={{ padding: '16px', borderBottom: '2px solid var(--sketch-black)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Title style={{ margin: 0 }}>Festipod</Title>
<span onClick={() => navigate('profile')} style={{ cursor: 'pointer', fontSize: 24 }}></span>
</div>
</div>
{/* Content */}
<div style={{ flex: 1, padding: 16, overflow: 'auto' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
<Text style={{ margin: 0, fontWeight: 'bold' }}>Événements à venir</Text>
<Text
style={{ margin: 0, fontSize: 14, cursor: 'pointer' }}
onClick={() => navigate('events')}
>
Voir tout
</Text>
</div>
<EventCard
title="Barbecue d'été"
date="Sam. 25 jan. · 16h00"
attendees={12}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Soirée jeux de société"
date="Ven. 31 jan. · 19h00"
attendees={8}
onClick={() => navigate('event-detail')}
/>
<EventCard
title="Randonnée"
date="Dim. 2 fév. · 9h00"
attendees={5}
onClick={() => navigate('event-detail')}
/>
<div style={{ marginTop: 24 }}>
<Button variant="primary" onClick={() => navigate('create-event')} style={{ width: '100%' }}>
+ Créer un événement
</Button>
</div>
</div>
{/* Bottom Nav */}
<NavBar
items={[
{ icon: '⌂', label: 'Accueil', active: true },
{ icon: '◎', label: 'Découvrir', onClick: () => navigate('events') },
{ icon: '+', label: 'Créer', onClick: () => navigate('create-event') },
{ icon: '☺', label: 'Profil', onClick: () => navigate('profile') },
]}
/>
</div>
);
}
+98
View File
@@ -0,0 +1,98 @@
import React, { useState } from 'react';
import { Header, Input, Text, Avatar, Checkbox, Button } from '../components/sketchy';
import type { ScreenProps } from './index';
interface Friend {
id: string;
name: string;
initials: string;
username: string;
}
const friends: Friend[] = [
{ id: '1', name: 'Alice Martin', initials: 'AM', username: '@alice' },
{ id: '2', name: 'Baptiste Morel', initials: 'BM', username: '@baptiste' },
{ id: '3', name: 'Camille Dubois', initials: 'CD', username: '@camille' },
{ id: '4', name: 'David Leroy', initials: 'DL', username: '@david' },
{ id: '5', name: 'Emma Bernard', initials: 'EB', username: '@emma' },
{ id: '6', name: 'François Petit', initials: 'FP', username: '@francois' },
];
export function InviteScreen({ navigate }: ScreenProps) {
const [selected, setSelected] = useState<Set<string>>(new Set());
const toggleFriend = (id: string) => {
const newSelected = new Set(selected);
if (newSelected.has(id)) {
newSelected.delete(id);
} else {
newSelected.add(id);
}
setSelected(newSelected);
};
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Inviter des amis"
left={<span onClick={() => navigate('event-detail')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Search */}
<div style={{ padding: '12px 16px', borderBottom: '1px solid var(--sketch-light-gray)' }}>
<Input placeholder="Rechercher un ami..." />
</div>
{/* Selected count */}
{selected.size > 0 && (
<div style={{
padding: '8px 16px',
background: 'var(--sketch-light-gray)',
fontSize: 14,
fontFamily: 'var(--font-sketch)',
}}>
{selected.size} ami{selected.size > 1 ? 's' : ''} sélectionné{selected.size > 1 ? 's' : ''}
</div>
)}
{/* Friends list */}
<div style={{ flex: 1, overflow: 'auto' }}>
{friends.map((friend) => (
<div
key={friend.id}
onClick={() => toggleFriend(friend.id)}
style={{
display: 'flex',
alignItems: 'center',
padding: '12px 16px',
borderBottom: '1px solid var(--sketch-light-gray)',
cursor: 'pointer',
background: selected.has(friend.id) ? 'var(--sketch-light-gray)' : 'transparent',
}}
>
<Avatar initials={friend.initials} size="sm" />
<div style={{ flex: 1, marginLeft: 12 }}>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{friend.name}</Text>
<Text className="user-content" style={{ margin: 0, fontSize: 14 }}>
{friend.username}
</Text>
</div>
<Checkbox checked={selected.has(friend.id)} />
</div>
))}
</div>
{/* Footer */}
<div style={{ padding: 16, borderTop: '2px solid var(--sketch-black)' }}>
<Button
variant="primary"
style={{ width: '100%' }}
onClick={() => navigate('event-detail')}
disabled={selected.size === 0}
>
Envoyer {selected.size > 0 ? `${selected.size} ` : ''}invitation{selected.size !== 1 ? 's' : ''}
</Button>
</div>
</div>
);
}
+39
View File
@@ -0,0 +1,39 @@
import React from 'react';
import { Button, Input, Title, Text } from '../components/sketchy';
import type { ScreenProps } from './index';
export function LoginScreen({ navigate }: ScreenProps) {
return (
<div style={{ padding: 24, display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center' }}>
<Title style={{ textAlign: 'center', fontSize: 32, marginBottom: 8 }}>Festipod</Title>
<Text style={{ textAlign: 'center', marginBottom: 32 }}>Créez et rejoignez des événements entre amis</Text>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
<div>
<Text style={{ marginBottom: 4, fontSize: 14 }}>Email</Text>
<Input type="email" placeholder="vous@exemple.com" />
</div>
<div>
<Text style={{ marginBottom: 4, fontSize: 14 }}>Mot de passe</Text>
<Input type="password" placeholder="••••••••" />
</div>
<Button variant="primary" onClick={() => navigate('home')}>
Se connecter
</Button>
<Text style={{ textAlign: 'center', fontSize: 14, color: 'var(--sketch-gray)' }}>
Mot de passe oublié ?
</Text>
</div>
</div>
<Text style={{ textAlign: 'center', fontSize: 14, color: 'var(--sketch-gray)' }}>
Pas encore de compte ? S'inscrire
</Text>
</div>
);
}
+131
View File
@@ -0,0 +1,131 @@
import React from 'react';
import { Header, Text, Button, Card, Avatar, Input, Divider } from '../components/sketchy';
import type { ScreenProps } from './index';
export function MeetingPointsScreen({ navigate }: ScreenProps) {
const meetingPoints = [
{
id: '1',
location: 'Café de la Place',
time: '30 min avant l\'événement',
host: { initials: 'MD', name: 'Marie' },
participants: 3,
},
{
id: '2',
location: 'Station de métro Bellecour',
time: '15h30',
host: { initials: 'JD', name: 'Jean' },
participants: 5,
},
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Points de rencontre"
left={<span onClick={() => navigate('event-detail')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
<Text style={{ color: 'var(--sketch-gray)', marginBottom: 16 }}>
Retrouvez d'autres participants avant l'événement pour y aller ensemble !
</Text>
{/* Existing meeting points */}
{meetingPoints.map((mp) => (
<Card key={mp.id} style={{ marginBottom: 12 }}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
<Avatar initials={mp.host.initials} size="sm" />
<div style={{ flex: 1 }}>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{mp.location}</Text>
<Text style={{ margin: '4px 0', fontSize: 14, color: 'var(--sketch-gray)' }}>
<span className="user-content">{mp.time}</span> · Proposé par <span className="user-content">{mp.host.name}</span>
</Text>
<Text style={{ margin: 0, fontSize: 13 }}>
{mp.participants} participant{mp.participants > 1 ? 's' : ''} inscrit{mp.participants > 1 ? 's' : ''}
</Text>
</div>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
<Button variant="primary" style={{ flex: 1 }}>Rejoindre</Button>
<Button style={{ flex: 1 }}>Voir les participants</Button>
</div>
</Card>
))}
<Divider />
{/* Create new meeting point */}
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Proposer un point de rencontre</Text>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Lieu</Text>
<Input placeholder="Ex: Café de la Gare, Entrée du parc..." />
</div>
<div>
<Text style={{ marginBottom: 6, fontSize: 14 }}>Heure</Text>
<div style={{ display: 'flex', gap: 8 }}>
<Button style={{ flex: 1 }}>30 min avant</Button>
<Button variant="primary" style={{ flex: 1 }}>1h avant</Button>
<Button style={{ flex: 1 }}>Personnalisé</Button>
</div>
</div>
<Button variant="primary" style={{ marginTop: 8 }}>
Créer le point de rencontre
</Button>
</div>
<Divider />
{/* QR Code exchange section */}
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Échanger vos contacts</Text>
<Text style={{ color: 'var(--sketch-gray)', marginBottom: 12, fontSize: 14 }}>
Partagez votre QR code avec les autres participants pour rester en contact.
</Text>
<Card style={{ textAlign: 'center', padding: 20 }}>
<div style={{
width: 120,
height: 120,
margin: '0 auto 12px',
border: '2px solid var(--sketch-black)',
borderRadius: 8,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'var(--sketch-white)',
}}>
<div style={{
width: 100,
height: 100,
background: `
repeating-linear-gradient(
0deg,
var(--sketch-black) 0px,
var(--sketch-black) 8px,
var(--sketch-white) 8px,
var(--sketch-white) 16px
)
`,
opacity: 0.3,
}} />
</div>
<Text style={{ margin: 0, fontWeight: 'bold' }}>Mon QR Code</Text>
<Text style={{ margin: '4px 0 0 0', fontSize: 13, color: 'var(--sketch-gray)' }}>
Scannez pour m'ajouter
</Text>
</Card>
<div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
<Button style={{ flex: 1 }}>Scanner un QR</Button>
<Button style={{ flex: 1 }}>Partager mon lien</Button>
</div>
</div>
</div>
);
}
+61
View File
@@ -0,0 +1,61 @@
import React from 'react';
import { Header, Avatar, Text, Input, Divider } from '../components/sketchy';
import type { ScreenProps } from './index';
export function ParticipantsListScreen({ navigate }: ScreenProps) {
const participants = [
{ initials: 'AM', name: 'Alice Martin', username: '@alice' },
{ initials: 'BM', name: 'Baptiste Morel', username: '@baptiste' },
{ initials: 'CD', name: 'Camille Dubois', username: '@camille' },
{ initials: 'DL', name: 'David Leroy', username: '@david' },
{ initials: 'EG', name: 'Emma Girard', username: '@emma' },
{ initials: 'FB', name: 'François Bernard', username: '@francois' },
{ initials: 'GM', name: 'Guillaume Mercier', username: '@guillaume' },
{ initials: 'HT', name: 'Hélène Thomas', username: '@helene' },
{ initials: 'MD', name: 'Marie Dupont', username: '@mariedupont' },
{ initials: 'PD', name: 'Pierre Durand', username: '@pierre' },
{ initials: 'SL', name: 'Sophie Lambert', username: '@sophie' },
{ initials: 'TM', name: 'Thomas Martin', username: '@thomas' },
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Participants (12)"
left={<span onClick={() => navigate('event-detail')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Search bar */}
<div style={{ padding: 16, borderBottom: '1px solid var(--sketch-light-gray)' }}>
<Input placeholder="Rechercher un participant..." />
</div>
{/* Participants list */}
<div style={{ flex: 1, overflow: 'auto' }}>
{participants.map((p, i) => (
<div
key={i}
onClick={() => navigate('user-profile')}
style={{
display: 'flex',
alignItems: 'center',
gap: 12,
padding: '12px 16px',
cursor: 'pointer',
borderBottom: '1px solid var(--sketch-light-gray)',
}}
>
<Avatar initials={p.initials} size="sm" />
<div style={{ flex: 1 }}>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{p.name}</Text>
<Text className="user-content" style={{ margin: 0, fontSize: 13 }}>
{p.username}
</Text>
</div>
<Text style={{ margin: 0, fontSize: 20, color: 'var(--sketch-gray)' }}></Text>
</div>
))}
</div>
</div>
);
}
+104
View File
@@ -0,0 +1,104 @@
import React from 'react';
import { Header, Avatar, Title, Text, Button, Card, Badge, Divider, NavBar } from '../components/sketchy';
import type { ScreenProps } from './index';
export function ProfileScreen({ navigate }: ScreenProps) {
const upcomingEvents = [
{ title: 'Barbecue d\'été', date: '25 jan.', role: 'Organisateur' },
{ title: 'Soirée jeux', date: '31 jan.', role: 'Participant' },
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Mon profil"
left={<span onClick={() => navigate('home')} style={{ cursor: 'pointer' }}></span>}
right={<span onClick={() => navigate('settings')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto' }}>
{/* Profile header */}
<div style={{ padding: 24, textAlign: 'center' }}>
<Avatar initials="MD" size="lg" />
<Title className="user-content" style={{ marginTop: 16, marginBottom: 4 }}>Marie Dupont</Title>
<Text className="user-content" style={{ margin: 0 }}>@mariedupont</Text>
<div style={{ display: 'flex', justifyContent: 'center', gap: 32, marginTop: 20 }}>
<div style={{ textAlign: 'center' }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>12</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Événements</Text>
</div>
<div style={{ textAlign: 'center', cursor: 'pointer' }} onClick={() => navigate('friends-list')}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>48</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Amis</Text>
</div>
<div style={{ textAlign: 'center' }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>156</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Participations</Text>
</div>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 20, justifyContent: 'center' }}>
<Button variant="primary">Modifier le profil</Button>
<Button onClick={() => navigate('share-profile')}>Partager</Button>
</div>
</div>
<Divider />
{/* Upcoming events */}
<div style={{ padding: 16 }}>
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Mes événements à venir</Text>
{upcomingEvents.map((event, i) => (
<Card key={i} onClick={() => navigate('event-detail')} style={{ marginBottom: 12 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{event.title}</Text>
<Text className="user-content" style={{ margin: '4px 0 0 0', fontSize: 14 }}>
{event.date}
</Text>
</div>
<Badge>{event.role}</Badge>
</div>
</Card>
))}
<Button style={{ width: '100%' }} onClick={() => navigate('events')}>
Voir tous les événements
</Button>
</div>
<Divider />
{/* Quick actions */}
<div style={{ padding: '0 16px 16px' }}>
<div
className="sketchy-list-item"
onClick={() => navigate('create-event')}
>
<span style={{ marginRight: 12 }}>+</span>
<Text style={{ margin: 0 }}>Créer un événement</Text>
</div>
<div className="sketchy-list-item" onClick={() => navigate('friends-list')}>
<span style={{ marginRight: 12 }}>👥</span>
<Text style={{ margin: 0 }}>Mes amis</Text>
</div>
<div className="sketchy-list-item">
<span style={{ marginRight: 12 }}>📜</span>
<Text style={{ margin: 0 }}>Événements passés</Text>
</div>
</div>
</div>
{/* Bottom Nav */}
<NavBar
items={[
{ icon: '⌂', label: 'Accueil', onClick: () => navigate('home') },
{ icon: '◎', label: 'Découvrir', onClick: () => navigate('events') },
{ icon: '+', label: 'Créer', onClick: () => navigate('create-event') },
{ icon: '☺', label: 'Profil', active: true },
]}
/>
</div>
);
}
+92
View File
@@ -0,0 +1,92 @@
import React, { useState } from 'react';
import { Header, Text, ListItem, Toggle, Divider, NavBar } from '../components/sketchy';
import type { ScreenProps } from './index';
export function SettingsScreen({ navigate }: ScreenProps) {
const [notifications, setNotifications] = useState(true);
const [darkMode, setDarkMode] = useState(false);
const [location, setLocation] = useState(true);
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Paramètres"
left={<span onClick={() => navigate('home')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto' }}>
<Text style={{ padding: '16px 16px 8px', fontSize: 14, color: 'var(--sketch-gray)', margin: 0 }}>
PRÉFÉRENCES
</Text>
<ListItem>
<div style={{ flex: 1 }}>
<Text style={{ margin: 0 }}>Notifications</Text>
<Text style={{ margin: 0, fontSize: 12, color: 'var(--sketch-gray)' }}>
Recevoir les notifications push
</Text>
</div>
<Toggle checked={notifications} onChange={setNotifications} />
</ListItem>
<ListItem>
<div style={{ flex: 1 }}>
<Text style={{ margin: 0 }}>Mode sombre</Text>
<Text style={{ margin: 0, fontSize: 12, color: 'var(--sketch-gray)' }}>
Activer le thème sombre
</Text>
</div>
<Toggle checked={darkMode} onChange={setDarkMode} />
</ListItem>
<ListItem>
<div style={{ flex: 1 }}>
<Text style={{ margin: 0 }}>Localisation</Text>
<Text style={{ margin: 0, fontSize: 12, color: 'var(--sketch-gray)' }}>
Autoriser l'accès à la position
</Text>
</div>
<Toggle checked={location} onChange={setLocation} />
</ListItem>
<Divider />
<Text style={{ padding: '16px 16px 8px', fontSize: 14, color: 'var(--sketch-gray)', margin: 0 }}>
COMPTE
</Text>
<ListItem onClick={() => navigate('profile')}>
<Text style={{ margin: 0, flex: 1 }}>Modifier le profil</Text>
<span>→</span>
</ListItem>
<ListItem>
<Text style={{ margin: 0, flex: 1 }}>Changer le mot de passe</Text>
<span>→</span>
</ListItem>
<ListItem>
<Text style={{ margin: 0, flex: 1 }}>Confidentialité</Text>
<span>→</span>
</ListItem>
<Divider />
<ListItem onClick={() => navigate('login')}>
<Text style={{ margin: 0, color: '#c00' }}>Se déconnecter</Text>
</ListItem>
</div>
{/* Bottom Nav */}
<NavBar
items={[
{ icon: '', label: 'Accueil', onClick: () => navigate('home') },
{ icon: '', label: 'Découvrir', onClick: () => navigate('events') },
{ icon: '+', label: 'Créer', onClick: () => navigate('create-event') },
{ icon: '', label: 'Profil', onClick: () => navigate('profile') },
]}
/>
</div>
);
}
+117
View File
@@ -0,0 +1,117 @@
import React from 'react';
import { Header, Text, Button, Card, Divider, Avatar } from '../components/sketchy';
import type { ScreenProps } from './index';
export function ShareProfileScreen({ navigate }: ScreenProps) {
const profileLink = 'festipod.app/u/mariedupont';
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Partager mon profil"
left={<span onClick={() => navigate('profile')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto', padding: 16 }}>
{/* QR Code */}
<Card style={{ textAlign: 'center', padding: 24 }}>
<div style={{
width: 180,
height: 180,
margin: '0 auto 16px',
border: '3px solid var(--sketch-black)',
borderRadius: 12,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'var(--sketch-white)',
position: 'relative',
}}>
{/* Simulated QR code pattern */}
<div style={{
width: 150,
height: 150,
background: `
linear-gradient(90deg, var(--sketch-black) 10%, transparent 10%, transparent 20%, var(--sketch-black) 20%, var(--sketch-black) 30%, transparent 30%, transparent 40%, var(--sketch-black) 40%, var(--sketch-black) 50%, transparent 50%, transparent 60%, var(--sketch-black) 60%, var(--sketch-black) 70%, transparent 70%, transparent 80%, var(--sketch-black) 80%, var(--sketch-black) 90%, transparent 90%),
linear-gradient(var(--sketch-black) 10%, transparent 10%, transparent 20%, var(--sketch-black) 20%, var(--sketch-black) 30%, transparent 30%, transparent 40%, var(--sketch-black) 40%, var(--sketch-black) 50%, transparent 50%, transparent 60%, var(--sketch-black) 60%, var(--sketch-black) 70%, transparent 70%, transparent 80%, var(--sketch-black) 80%, var(--sketch-black) 90%, transparent 90%)
`,
backgroundSize: '15px 15px',
opacity: 0.8,
}} />
{/* Center avatar */}
<div style={{
position: 'absolute',
background: 'var(--sketch-white)',
padding: 4,
borderRadius: '50%',
}}>
<Avatar initials="MD" size="sm" />
</div>
</div>
<Text className="user-content" style={{ fontWeight: 'bold', margin: '0 0 4px 0' }}>Marie Dupont</Text>
<Text style={{ color: 'var(--sketch-gray)', margin: 0, fontSize: 14 }}>
Scannez pour me retrouver sur Festipod
</Text>
</Card>
<Divider />
{/* Link section */}
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Mon lien de profil</Text>
<Card style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<Text style={{
margin: 0,
flex: 1,
fontSize: 14,
wordBreak: 'break-all',
}}>
{profileLink}
</Text>
<Button style={{ flexShrink: 0 }}>Copier</Button>
</Card>
<Divider />
{/* Share options */}
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Partager via</Text>
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<Button style={{ width: '100%', justifyContent: 'flex-start', textAlign: 'left' }}>
<span style={{ marginRight: 12 }}>📱</span>
Message / SMS
</Button>
<Button style={{ width: '100%', justifyContent: 'flex-start', textAlign: 'left' }}>
<span style={{ marginRight: 12 }}></span>
E-mail
</Button>
<Button style={{ width: '100%', justifyContent: 'flex-start', textAlign: 'left' }}>
<span style={{ marginRight: 12 }}>📋</span>
Copier le lien
</Button>
</div>
<Divider />
{/* Stats */}
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Statistiques de parrainage</Text>
<Card>
<div style={{ display: 'flex', justifyContent: 'space-around', textAlign: 'center' }}>
<div>
<Text style={{ fontWeight: 'bold', fontSize: 24, margin: 0, color: 'var(--sketch-black)' }}>12</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>
Personnes parrainées
</Text>
</div>
<div>
<Text style={{ fontWeight: 'bold', fontSize: 24, margin: 0, color: 'var(--sketch-black)' }}>47</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>
Scans du QR code
</Text>
</div>
</div>
</Card>
</div>
</div>
);
}
+95
View File
@@ -0,0 +1,95 @@
import React from 'react';
import { Header, Avatar, Title, Text, Button, Card, Badge, Divider } from '../components/sketchy';
import type { ScreenProps } from './index';
export function UserProfileScreen({ navigate }: ScreenProps) {
const pastEvents = [
{ title: 'Atelier poterie', date: '15 déc. 2025', role: 'Participant' },
{ title: 'Festival d\'été', date: '20 juil. 2025', role: 'Organisateur' },
{ title: 'Randonnée collective', date: '5 mai 2025', role: 'Participant' },
];
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<Header
title="Profil"
left={<span onClick={() => navigate('event-detail')} style={{ cursor: 'pointer' }}></span>}
/>
{/* Content */}
<div style={{ flex: 1, overflow: 'auto' }}>
{/* User profile header */}
<div style={{ padding: 24, textAlign: 'center' }}>
<Avatar initials="JD" size="lg" />
<Title className="user-content" style={{ marginTop: 16, marginBottom: 4 }}>Jean Durand</Title>
<Text className="user-content" style={{ margin: 0 }}>@jeandurand</Text>
<div style={{ display: 'flex', justifyContent: 'center', gap: 32, marginTop: 20 }}>
<div style={{ textAlign: 'center' }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>8</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Événements</Text>
</div>
<div style={{ textAlign: 'center' }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>23</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Contacts</Text>
</div>
<div style={{ textAlign: 'center' }}>
<Text style={{ fontWeight: 'bold', margin: 0 }}>42</Text>
<Text style={{ fontSize: 12, color: 'var(--sketch-gray)', margin: 0 }}>Participations</Text>
</div>
</div>
<div style={{ display: 'flex', gap: 8, marginTop: 20, justifyContent: 'center' }}>
<Button variant="primary">Ajouter au réseau</Button>
<Button>Contacter</Button>
</div>
</div>
<Divider />
{/* Common events */}
<div style={{ padding: 16 }}>
<Text style={{ fontWeight: 'bold', marginBottom: 4 }}>Événements en commun</Text>
<Text style={{ fontSize: 13, color: 'var(--sketch-gray)', marginBottom: 12 }}>
Vous avez participé à 3 événements ensemble
</Text>
{pastEvents.map((event, i) => (
<Card key={i} onClick={() => navigate('event-detail')} style={{ marginBottom: 12 }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Text className="user-content" style={{ margin: 0, fontWeight: 'bold' }}>{event.title}</Text>
<Text className="user-content" style={{ margin: '4px 0 0 0', fontSize: 14 }}>
{event.date}
</Text>
</div>
<Badge>{event.role}</Badge>
</div>
</Card>
))}
</div>
<Divider />
{/* Contact form section */}
<div style={{ padding: 16 }}>
<Text style={{ fontWeight: 'bold', marginBottom: 12 }}>Envoyer un message</Text>
<div style={{
border: '2px solid var(--sketch-black)',
borderRadius: '255px 15px 225px 15px/15px 225px 15px 255px',
padding: 12,
minHeight: 80,
fontFamily: 'var(--font-sketch)',
fontSize: 14,
color: 'var(--sketch-gray)',
}}>
Écrivez votre message ici...
</div>
<Button variant="primary" style={{ width: '100%', marginTop: 12 }}>
Envoyer
</Button>
</div>
</div>
</div>
);
}
+77
View File
@@ -0,0 +1,77 @@
import React from 'react';
import { HomeScreen } from './HomeScreen';
import { LoginScreen } from './LoginScreen';
import { ProfileScreen } from './ProfileScreen';
import { SettingsScreen } from './SettingsScreen';
import { UserProfileScreen } from './UserProfileScreen';
import { EventsScreen } from './EventsScreen';
import { EventDetailScreen } from './EventDetailScreen';
import { CreateEventScreen } from './CreateEventScreen';
import { InviteScreen } from './InviteScreen';
import { ParticipantsListScreen } from './ParticipantsListScreen';
import { MeetingPointsScreen } from './MeetingPointsScreen';
import { FriendsListScreen } from './FriendsListScreen';
import { ShareProfileScreen } from './ShareProfileScreen';
export interface Screen {
id: string;
name: string;
component: React.ComponentType<ScreenProps>;
}
export interface ScreenGroup {
id: string;
name: string;
screens: Screen[];
}
export interface ScreenProps {
navigate: (screenId: string) => void;
}
export const screenGroups: ScreenGroup[] = [
{
id: 'home',
name: 'Accueil',
screens: [
{ id: 'home', name: 'Accueil', component: HomeScreen },
],
},
{
id: 'events',
name: 'Événements',
screens: [
{ id: 'events', name: 'Découvrir', component: EventsScreen },
{ id: 'event-detail', name: 'Détail événement', component: EventDetailScreen },
{ id: 'create-event', name: 'Créer événement', component: CreateEventScreen },
{ id: 'invite', name: 'Inviter des amis', component: InviteScreen },
{ id: 'participants-list', name: 'Liste des participants', component: ParticipantsListScreen },
{ id: 'meeting-points', name: 'Points de rencontre', component: MeetingPointsScreen },
],
},
{
id: 'user',
name: 'Utilisateur',
screens: [
{ id: 'profile', name: 'Mon profil', component: ProfileScreen },
{ id: 'user-profile', name: 'Profil d\'un utilisateur', component: UserProfileScreen },
{ id: 'friends-list', name: 'Mon réseau', component: FriendsListScreen },
{ id: 'share-profile', name: 'Partager mon profil', component: ShareProfileScreen },
],
},
{
id: 'general',
name: 'Général',
screens: [
{ id: 'login', name: 'Connexion', component: LoginScreen },
{ id: 'settings', name: 'Paramètres', component: SettingsScreen },
],
},
];
// Flat list of all screens for compatibility
export const screens: Screen[] = screenGroups.flatMap(group => group.screens);
export function getScreen(id: string): Screen | undefined {
return screens.find(s => s.id === id);
}