Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4c49e9f
docs: define frontend recovery plan and canonical design sources
bychrisr Feb 12, 2026
41a3106
feat(frontend): implement netflix-themed web/admin flows and route split
bychrisr Feb 12, 2026
44b71e9
test(e2e): stabilize smoke suite with isolated runtime ports
bychrisr Feb 12, 2026
4de3a31
feat(design-system): add complete atomic component architecture
bychrisr Feb 12, 2026
41c1230
chore(design-system): alinhar tokens tipograficos ao figma
bychrisr Feb 12, 2026
60d700e
feat(design-system): adicionar dropdown e pattern browse by language
bychrisr Feb 12, 2026
a42464e
design-system: tokenizar Input e patterns de autenticação
bychrisr Feb 12, 2026
c32008a
admin: aplicar SignInEmailPasswordPattern na página de login
bychrisr Feb 12, 2026
518af53
design-system: adicionar catálogo de ícones e infraestrutura de assets
bychrisr Feb 12, 2026
5970bb2
feat(design-system): add MovieInfo pattern with tokenized layout
bychrisr Feb 12, 2026
97ef138
feat(design-system): adapt action button variants and tokenized sizes
bychrisr Feb 12, 2026
e361d59
chore(design-system): move and register video player icon set
bychrisr Feb 12, 2026
1829e31
chore(design-system): move and register movie preview icon sets
bychrisr Feb 12, 2026
03f948f
chore(design-system): move and register hero banner preview icon sets
bychrisr Feb 12, 2026
4e0cc18
feat(design-system): add video interaction patterns
bychrisr Feb 12, 2026
c60550d
feat(auth): apply complete sign-in pattern to admin login
bychrisr Feb 12, 2026
71c6d65
fix(auth): refine sign-in spacing and full-width controls
bychrisr Feb 12, 2026
9d10427
feat(design-system): add landing and home header organisms
bychrisr Feb 12, 2026
6565554
feat(design-system): add FAQ and hero banner patterns with tokens
bychrisr Feb 12, 2026
a32e391
feat(design-system): add home hero organism from homepage reference
bychrisr Feb 12, 2026
821e796
feat(design-system): add HeroMobile organism and mobile hero tokens
bychrisr Feb 12, 2026
6f8c058
feat(design-system): add anatomical MoviePreview patterns and card pr…
bychrisr Feb 12, 2026
57b1bfb
chore(ds): phase-0 baseline before UserProfileAvatar
bychrisr Feb 12, 2026
950776e
feat(ds/profile): add profile avatar and menu tokens
bychrisr Feb 12, 2026
f967b83
feat(ds/profile): add avatar atom and profile menu molecules
bychrisr Feb 12, 2026
c81dd6b
feat(ds/profile): add user profiles and profile menu organisms
bychrisr Feb 12, 2026
5074849
feat(ds/player): implement anatomical video player components and pat…
bychrisr Feb 12, 2026
40e3d43
feat(apps): adopt design-system package usage and add showcase page
bychrisr Feb 12, 2026
70bfd30
chore: checkpoint after user-profile, video-player, showcase and app …
bychrisr Feb 12, 2026
8cb2d81
chore(ds): finalize pending design-system updates and smoke alignment
bychrisr Feb 12, 2026
69700f2
fix(ci): replace workspace protocol dependency for npm ci compatibility
bychrisr Feb 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ services/api/.data/
# E2E artifacts
playwright-report/
test-results/

# Local MCP config
mcp.toml
1 change: 1 addition & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"typecheck": "echo 'admin typecheck not configured yet'"
},
"dependencies": {
"@flix/design-system": "0.1.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.4"
Expand Down
14 changes: 12 additions & 2 deletions apps/admin/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
import { Navigate, Route, Routes } from 'react-router-dom';
import { ProtectedRoute } from './routes/ProtectedRoute.jsx';
import { LoginPage } from './pages/LoginPage.jsx';
import { DashboardPage } from './pages/DashboardPage.jsx';
import { DashboardHomePage } from './pages/DashboardHomePage.jsx';
import { EventsPage } from './pages/EventsPage.jsx';
import { NewEventPage } from './pages/NewEventPage.jsx';
import { EditEventPage } from './pages/EditEventPage.jsx';
import { LessonsPage } from './pages/LessonsPage.jsx';
import { QuizzesPage } from './pages/QuizzesPage.jsx';

export const App = () => (
<Routes>
<Route path="/login" element={<LoginPage />} />

<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<DashboardPage />} />
<Route path="/dashboard" element={<DashboardHomePage />} />
<Route path="/eventos" element={<EventsPage />} />
<Route path="/eventos/novo" element={<NewEventPage />} />
<Route path="/eventos/:id/editar" element={<EditEventPage />} />
<Route path="/aulas" element={<LessonsPage />} />
<Route path="/quizzes" element={<QuizzesPage />} />
</Route>

<Route path="*" element={<Navigate to="/dashboard" replace />} />
Expand Down
1 change: 1 addition & 0 deletions apps/admin/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { App } from './App.jsx';
import { AuthProvider } from './auth/AuthContext.jsx';
import '@flix/design-system/tokens/theme.css';
import './styles.css';

ReactDOM.createRoot(document.getElementById('root')).render(
Expand Down
3 changes: 3 additions & 0 deletions apps/admin/src/pages/DashboardHomePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DashboardPage } from './DashboardPage.jsx';

export const DashboardHomePage = () => <DashboardPage section="dashboard" />;
169 changes: 152 additions & 17 deletions apps/admin/src/pages/DashboardPage.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useMemo, useState } from 'react';
import { NavLink, useNavigate, useParams } from 'react-router-dom';
import { useAuth } from '../auth/AuthContext.jsx';
import {
createEvent,
Expand Down Expand Up @@ -95,7 +96,18 @@ const buildEventUpdatePayload = (form) => ({
logoUrl: form.logoUrl ? form.logoUrl : null,
});

export const DashboardPage = () => {
const pageTitles = {
dashboard: 'Dashboard',
eventos: 'Eventos',
'eventos-new': 'Eventos / Novo',
'eventos-edit': 'Eventos / Editar',
aulas: 'Aulas',
quizzes: 'Quizzes',
};

export const DashboardPage = ({ section = 'dashboard' }) => {
const navigate = useNavigate();
const { id: routeEventId } = useParams();
const { session, logout } = useAuth();
const token = session?.accessToken;

Expand All @@ -119,6 +131,10 @@ export const DashboardPage = () => {
const [status, setStatus] = useState('');
const [error, setError] = useState('');

const isEventosSection = ['eventos', 'eventos-new', 'eventos-edit'].includes(section);
const isEventCreateMode = section === 'eventos-new';
const isEventEditMode = section === 'eventos-edit';

const selectedEvent = useMemo(
() => events.find((event) => event.id === selectedEventId) ?? null,
[events, selectedEventId],
Expand Down Expand Up @@ -248,6 +264,26 @@ export const DashboardPage = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedEventId, selectedLessonId]);

useEffect(() => {
if (isEventCreateMode) {
setSelectedEventId('');
setEventForm(defaultEventForm);
setBrandingPreview(null);
setBrandingRollback(null);
setBrandingError('');
return;
}

if (isEventEditMode && routeEventId) {
setSelectedEventId(routeEventId);
}
}, [isEventCreateMode, isEventEditMode, routeEventId]);

useEffect(() => {
if (!isEventEditMode || !selectedEvent) return;
setEventForm(mapEventToForm(selectedEvent));
}, [isEventEditMode, selectedEvent]);

const handleCreateEvent = async (event) => {
event.preventDefault();
resetFeedback();
Expand Down Expand Up @@ -700,14 +736,29 @@ export const DashboardPage = () => {
<main className="dashboard-layout">
<header className="dashboard-header">
<div>
<h1>Admin Content Operations</h1>
<p>Manage events, lessons, materials, quizzes, and branding from one place.</p>
<h1>{pageTitles[section] ?? 'Admin Content Operations'}</h1>
<p>Fluxo administrativo orientado por rotas do workflow oficial.</p>
</div>
<button type="button" onClick={logout}>
Logout
</button>
</header>

<nav className="admin-nav">
<NavLink to="/dashboard" className={({ isActive }) => (isActive ? 'active' : '')}>
Dashboard
</NavLink>
<NavLink to="/eventos" className={({ isActive }) => (isActive ? 'active' : '')}>
Eventos
</NavLink>
<NavLink to="/aulas" className={({ isActive }) => (isActive ? 'active' : '')}>
Aulas
</NavLink>
<NavLink to="/quizzes" className={({ isActive }) => (isActive ? 'active' : '')}>
Quizzes
</NavLink>
</nav>

<section className="dashboard-grid two-col">
<article>
<h2>Runtime</h2>
Expand All @@ -726,7 +777,31 @@ export const DashboardPage = () => {
</article>
</section>

<section className="dashboard-grid two-col">
{section === 'dashboard' ? (
<section className="dashboard-grid two-col">
<article>
<h2>Command Center</h2>
<p className="muted">Use o menu para operar cada etapa com foco por contexto.</p>
<div className="inline-actions">
<NavLink to="/eventos">Abrir Eventos</NavLink>
<NavLink to="/aulas">Abrir Aulas</NavLink>
<NavLink to="/quizzes">Abrir Quizzes</NavLink>
</div>
</article>
<article>
<h2>Selecao Atual</h2>
<p className="muted">
Evento: <strong>{selectedEvent?.title ?? 'Nenhum'}</strong>
</p>
<p className="muted">
Aula: <strong>{selectedLesson?.title ?? 'Nenhuma'}</strong>
</p>
</article>
</section>
) : null}

{isEventosSection ? (
<section className="dashboard-grid two-col">
<article>
<h2>Events + Branding</h2>
<form className="stack-form" onSubmit={handleCreateEvent}>
Expand Down Expand Up @@ -919,34 +994,78 @@ export const DashboardPage = () => {
)}

<div className="inline-actions">
<button type="submit">Create event</button>
<button type="button" onClick={handleLoadEventForEdit}>
Load selected
</button>
<button type="button" onClick={handleUpdateEvent}>
Update selected
</button>
<button type="button" className="danger" onClick={handleDeleteEvent}>
Delete selected
</button>
{isEventCreateMode ? (
<>
<button type="submit">Create event</button>
<button type="button" onClick={() => setEventForm(defaultEventForm)}>
Clear form
</button>
</>
) : (
<>
<button type="submit">Create event</button>
<button type="button" onClick={handleLoadEventForEdit}>
Load selected
</button>
<button type="button" onClick={handleUpdateEvent}>
Update selected
</button>
<button type="button" className="danger" onClick={handleDeleteEvent}>
Delete selected
</button>
</>
)}
</div>
</form>

<div className="inline-actions">
<NavLink to="/eventos/novo">Novo evento</NavLink>
<NavLink to="/eventos">Painel eventos</NavLink>
{selectedEventId ? <NavLink to={`/eventos/${selectedEventId}/editar`}>Editar selecionado</NavLink> : null}
</div>

<ul className="select-list">
{events.map((item) => (
<li key={item.id}>
<button
type="button"
className={selectedEventId === item.id ? 'active' : ''}
onClick={() => setSelectedEventId(item.id)}
onClick={() => {
setSelectedEventId(item.id);
if (isEventEditMode) {
navigate(`/eventos/${item.id}/editar`);
}
}}
>
{item.title} ({item.visibility})
</button>
</li>
))}
</ul>
</article>
<article>
<h2>Selected Event Context</h2>
{selectedEvent ? (
<div className="payload-preview">
<p>
<strong>{selectedEvent.title}</strong>
</p>
<p className="muted">Slug: {selectedEvent.slug}</p>
<p className="muted">Visibility: {selectedEvent.visibility}</p>
</div>
) : (
<p className="muted">
{isEventEditMode
? 'Select an event or use a valid /eventos/:id/editar URL.'
: 'Select an event to continue with lesson and quiz flows.'}
</p>
)}
</article>
</section>
) : null}

{section === 'aulas' ? (
<section className="dashboard-grid two-col">
<article>
<h2>Lessons</h2>
<form className="stack-form" onSubmit={handleCreateLesson}>
Expand Down Expand Up @@ -1025,9 +1144,7 @@ export const DashboardPage = () => {
))}
</ul>
</article>
</section>

<section className="dashboard-grid two-col">
<article>
<h2>Materials</h2>
<form className="stack-form" onSubmit={handleCreateMaterial}>
Expand Down Expand Up @@ -1076,7 +1193,11 @@ export const DashboardPage = () => {
))}
</ul>
</article>
</section>
) : null}

{section === 'quizzes' ? (
<section className="dashboard-grid two-col">
<article>
<h2>Quizzes</h2>
<form className="stack-form" onSubmit={handleCreateQuiz}>
Expand Down Expand Up @@ -1146,7 +1267,21 @@ export const DashboardPage = () => {
</div>
) : null}
</article>
<article>
<h2>Lesson Context</h2>
{selectedLesson ? (
<div className="payload-preview">
<p>
<strong>{selectedLesson.title}</strong>
</p>
<p className="muted">Slug: {selectedLesson.slug}</p>
</div>
) : (
<p className="muted">Select event and lesson in /aulas before creating quizzes.</p>
)}
</article>
</section>
) : null}
</main>
);
};
3 changes: 3 additions & 0 deletions apps/admin/src/pages/EditEventPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DashboardPage } from './DashboardPage.jsx';

export const EditEventPage = () => <DashboardPage section="eventos-edit" />;
3 changes: 3 additions & 0 deletions apps/admin/src/pages/EventsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DashboardPage } from './DashboardPage.jsx';

export const EventsPage = () => <DashboardPage section="eventos" />;
3 changes: 3 additions & 0 deletions apps/admin/src/pages/LessonsPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DashboardPage } from './DashboardPage.jsx';

export const LessonsPage = () => <DashboardPage section="aulas" />;
Loading