forked from RiseUP/riseup_squad_03
feat: destaca dados mockados e WIP no app
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { BrandLogo } from './Brand.jsx'
|
import { BrandLogo } from './Brand.jsx'
|
||||||
|
import { FeatureLegend } from './FeatureState.jsx'
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ href: '/inicio', label: 'Painel', icon: 'pulse', activePaths: ['/inicio', '/home', '/dashboard'] },
|
{ href: '/inicio', label: 'Painel', icon: 'pulse', activePaths: ['/inicio', '/home', '/dashboard'] },
|
||||||
@@ -176,6 +177,9 @@ export function AppShell({ children, currentPath, navigate, routeTitle }) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="w-full px-4 py-6 md:px-8 md:py-8" id="app-content">
|
<main className="w-full px-4 py-6 md:px-8 md:py-8" id="app-content">
|
||||||
|
<div className="mb-6">
|
||||||
|
<FeatureLegend />
|
||||||
|
</div>
|
||||||
<div className="sr-only" aria-live="polite">
|
<div className="sr-only" aria-live="polite">
|
||||||
{pageTitle}
|
{pageTitle}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
39
src/components/FeatureState.jsx
Normal file
39
src/components/FeatureState.jsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { featureStateStyles } from './featureStateStyles.js'
|
||||||
|
|
||||||
|
export function FeatureBadge({ className = '', status = 'partial', text }) {
|
||||||
|
const current = featureStateStyles[status] || featureStateStyles.partial
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className={`inline-flex items-center rounded-full border px-2.5 py-1 text-[10px] font-bold uppercase tracking-[0.14em] ${current.badge} ${className}`}
|
||||||
|
>
|
||||||
|
{text || current.label}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeatureCallout({ className = '', description, status = 'partial', title }) {
|
||||||
|
const current = featureStateStyles[status] || featureStateStyles.partial
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`rounded-2xl border px-4 py-3 ${current.panel} ${className}`}>
|
||||||
|
<div className="flex flex-wrap items-center gap-3">
|
||||||
|
<FeatureBadge status={status} />
|
||||||
|
{title ? <p className={`text-sm font-semibold ${current.title}`}>{title}</p> : null}
|
||||||
|
</div>
|
||||||
|
{description ? <p className="mt-2 text-sm leading-6 text-[#d4d4d4]">{description}</p> : null}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FeatureLegend() {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap items-center gap-2 rounded-xl border border-[#404040] bg-[#202020] px-3 py-2">
|
||||||
|
<span className="text-[11px] font-semibold uppercase tracking-[0.14em] text-[#a3a3a3]">Legenda</span>
|
||||||
|
<FeatureBadge status="live" />
|
||||||
|
<FeatureBadge status="partial" />
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
<FeatureBadge status="wip" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
31
src/components/featureStateStyles.js
Normal file
31
src/components/featureStateStyles.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
export const featureStateStyles = {
|
||||||
|
live: {
|
||||||
|
badge: 'border-emerald-500/40 bg-emerald-500/15 text-emerald-300',
|
||||||
|
panel: 'border-emerald-500/35 bg-emerald-500/8',
|
||||||
|
title: 'text-emerald-300',
|
||||||
|
label: 'Integrado',
|
||||||
|
},
|
||||||
|
partial: {
|
||||||
|
badge: 'border-sky-500/40 bg-sky-500/15 text-sky-300',
|
||||||
|
panel: 'border-sky-500/35 bg-sky-500/8',
|
||||||
|
title: 'text-sky-300',
|
||||||
|
label: 'Parcial',
|
||||||
|
},
|
||||||
|
mock: {
|
||||||
|
badge: 'border-amber-500/40 bg-amber-500/15 text-amber-300',
|
||||||
|
panel: 'border-amber-500/35 bg-amber-500/8',
|
||||||
|
title: 'text-amber-300',
|
||||||
|
label: 'Mockado',
|
||||||
|
},
|
||||||
|
wip: {
|
||||||
|
badge: 'border-rose-500/40 bg-rose-500/15 text-rose-300',
|
||||||
|
panel: 'border-rose-500/35 bg-rose-500/8',
|
||||||
|
title: 'text-rose-300',
|
||||||
|
label: 'WIP',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export function featurePanelClass(status = 'partial') {
|
||||||
|
const current = featureStateStyles[status] || featureStateStyles.partial
|
||||||
|
return current.panel
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import { appointmentRepository } from '../repositories/appointmentRepository.js'
|
import { appointmentRepository } from '../repositories/appointmentRepository.js'
|
||||||
|
import { FeatureBadge, FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { patientRepository } from '../repositories/patientRepository.js'
|
import { patientRepository } from '../repositories/patientRepository.js'
|
||||||
import { professionalRepository } from '../repositories/professionalRepository.js'
|
import { professionalRepository } from '../repositories/professionalRepository.js'
|
||||||
|
|
||||||
@@ -88,6 +90,12 @@ useEffect(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex max-w-[1180px] flex-col gap-8 text-[#e5e5e5]">
|
<div className="mx-auto flex max-w-[1180px] flex-col gap-8 text-[#e5e5e5]">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Listagem e criação de agendamentos usam API. Linha do tempo, resumo preditivo e calendário semanal ainda são visuais simulados."
|
||||||
|
status="partial"
|
||||||
|
title="Agenda com partes reais e partes mockadas"
|
||||||
|
/>
|
||||||
|
|
||||||
<section className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
<section className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-[32px] font-bold leading-8 tracking-[-0.02em] text-[#e5e5e5]">
|
<h1 className="text-[32px] font-bold leading-8 tracking-[-0.02em] text-[#e5e5e5]">
|
||||||
@@ -116,7 +124,7 @@ useEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="grid gap-4 lg:grid-cols-5">
|
<section className={`grid gap-4 lg:grid-cols-5 ${featurePanelClass('mock')}`}>
|
||||||
{weekDays.map((day) => (
|
{weekDays.map((day) => (
|
||||||
<button
|
<button
|
||||||
className={`rounded-2xl border p-4 text-left transition ${
|
className={`rounded-2xl border p-4 text-left transition ${
|
||||||
@@ -137,10 +145,13 @@ useEffect(() => {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="grid gap-6 xl:grid-cols-[1.45fr_0.85fr]">
|
<section className="grid gap-6 xl:grid-cols-[1.45fr_0.85fr]">
|
||||||
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)]">
|
<div className={`rounded-2xl border bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)] ${featurePanelClass('live')}`}>
|
||||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-base font-bold leading-6 text-[#e5e5e5]">Terça, 07 abril</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold leading-6 text-[#e5e5e5]">Terça, 07 abril</h2>
|
||||||
|
<FeatureBadge status="live" />
|
||||||
|
</div>
|
||||||
<p className="mt-1 text-sm leading-5 text-[#a3a3a3]">
|
<p className="mt-1 text-sm leading-5 text-[#a3a3a3]">
|
||||||
Visualização: {activeView.toLowerCase()} | {visibleAppointments.length} registros no filtro
|
Visualização: {activeView.toLowerCase()} | {visibleAppointments.length} registros no filtro
|
||||||
</p>
|
</p>
|
||||||
@@ -202,8 +213,11 @@ useEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-6">
|
<div className="grid gap-6">
|
||||||
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5">
|
<div className={`rounded-2xl border bg-[#262626] p-5 ${featurePanelClass('mock')}`}>
|
||||||
<h2 className="text-base font-bold text-[#e5e5e5]">Linha do tempo</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold text-[#e5e5e5]">Linha do tempo</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<div className="mt-5 grid gap-1">
|
<div className="mt-5 grid gap-1">
|
||||||
{timeline.map((item) => (
|
{timeline.map((item) => (
|
||||||
<button
|
<button
|
||||||
@@ -223,8 +237,11 @@ useEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5">
|
<div className={`rounded-2xl border bg-[#262626] p-5 ${featurePanelClass('mock')}`}>
|
||||||
<h2 className="text-base font-bold text-[#e5e5e5]">Resumo preditivo</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold text-[#e5e5e5]">Resumo preditivo</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<div className="mt-5 grid gap-3">
|
<div className="mt-5 grid gap-3">
|
||||||
{queue.map((item) => (
|
{queue.map((item) => (
|
||||||
<div className="flex items-center justify-between rounded-md bg-[#2a2a2a] px-4 py-3" key={item.label}>
|
<div className="flex items-center justify-between rounded-md bg-[#2a2a2a] px-4 py-3" key={item.label}>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
import { analyticsRepository } from '../repositories/analyticsRepository.js'
|
import { analyticsRepository } from '../repositories/analyticsRepository.js'
|
||||||
|
|
||||||
const periods = [
|
const periods = [
|
||||||
@@ -25,6 +26,12 @@ export function AnalyticsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6">
|
<div className="mx-auto max-w-7xl space-y-6">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Os indicadores, gráficos e rankings desta tela ainda vêm de dados mockados."
|
||||||
|
status="mock"
|
||||||
|
title="Analytics ainda é demonstrativo"
|
||||||
|
/>
|
||||||
|
|
||||||
<section className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<section className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Relatórios & Analytics</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Relatórios & Analytics</h1>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useState } from 'react'
|
|||||||
import { authRepository } from '../repositories/authRepository.js'
|
import { authRepository } from '../repositories/authRepository.js'
|
||||||
|
|
||||||
import { BrandLogo } from '../components/Brand.jsx'
|
import { BrandLogo } from '../components/Brand.jsx'
|
||||||
|
import { FeatureBadge, FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
import loginClinicImage from '../assets/figma/login-clinic.png'
|
import loginClinicImage from '../assets/figma/login-clinic.png'
|
||||||
|
|
||||||
export function LoginPage({ navigate }) {
|
export function LoginPage({ navigate }) {
|
||||||
@@ -163,6 +164,7 @@ export function LoginPage({ navigate }) {
|
|||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
dev · credenciais
|
dev · credenciais
|
||||||
|
<FeatureBadge className="border-white/20 bg-white/10 text-white/70" status="mock" text="mock" />
|
||||||
<span aria-hidden="true" className="text-[9px]">
|
<span aria-hidden="true" className="text-[9px]">
|
||||||
^
|
^
|
||||||
</span>
|
</span>
|
||||||
@@ -181,6 +183,12 @@ export function RegisterPage({ navigate }) {
|
|||||||
description="Crie um acesso mockado para navegar pelo ambiente da clínica."
|
description="Crie um acesso mockado para navegar pelo ambiente da clínica."
|
||||||
title="Criar acesso"
|
title="Criar acesso"
|
||||||
>
|
>
|
||||||
|
<FeatureCallout
|
||||||
|
className="mt-6"
|
||||||
|
description="Cadastro ainda é apenas demonstrativo e não cria conta real."
|
||||||
|
status="mock"
|
||||||
|
title="Fluxo mockado"
|
||||||
|
/>
|
||||||
<form
|
<form
|
||||||
className="mt-8 grid gap-5"
|
className="mt-8 grid gap-5"
|
||||||
onSubmit={(event) => {
|
onSubmit={(event) => {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import loginClinicImage from '../assets/figma/login-clinic.png'
|
import loginClinicImage from '../assets/figma/login-clinic.png'
|
||||||
|
import { FeatureBadge, FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { homeRepository } from '../repositories/homeRepository.js'
|
import { homeRepository } from '../repositories/homeRepository.js'
|
||||||
|
|
||||||
export function HomePage({ navigate }) {
|
export function HomePage({ navigate }) {
|
||||||
@@ -6,6 +8,12 @@ export function HomePage({ navigate }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex max-w-[1180px] flex-col gap-8 text-[#e5e5e5]">
|
<div className="mx-auto flex max-w-[1180px] flex-col gap-8 text-[#e5e5e5]">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Painel, métricas, insights e relatórios desta tela ainda usam dados mockados."
|
||||||
|
status="mock"
|
||||||
|
title="Visão geral ainda não está ligada à API"
|
||||||
|
/>
|
||||||
|
|
||||||
<section className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
<section className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-[32px] font-bold leading-8 tracking-[-0.02em] text-[#e5e5e5]">
|
<h1 className="text-[32px] font-bold leading-8 tracking-[-0.02em] text-[#e5e5e5]">
|
||||||
@@ -41,14 +49,17 @@ export function HomePage({ navigate }) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="grid gap-6 xl:grid-cols-[1.7fr_0.9fr]">
|
<section className="grid gap-6 xl:grid-cols-[1.7fr_0.9fr]">
|
||||||
<div className="rounded-2xl border border-[#3b82f6] bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)]">
|
<div className={`rounded-2xl border bg-[#262626] p-5 shadow-[0_1px_3px_rgba(0,0,0,0.2)] ${featurePanelClass('mock')}`}>
|
||||||
<div className="flex items-start justify-between gap-4">
|
<div className="flex items-start justify-between gap-4">
|
||||||
<div className="flex items-start gap-4">
|
<div className="flex items-start gap-4">
|
||||||
<div className="grid size-12 shrink-0 place-items-center rounded-md bg-[#3b82f6] text-white">
|
<div className="grid size-12 shrink-0 place-items-center rounded-md bg-[#3b82f6] text-white">
|
||||||
<SparkLineIcon className="size-6" />
|
<SparkLineIcon className="size-6" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-base font-bold leading-6 text-[#3b82f6]">Insights de IA</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold leading-6 text-[#3b82f6]">Insights de IA</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<p className="mt-1 text-sm font-medium leading-5 text-[#a3a3a3]">
|
<p className="mt-1 text-sm font-medium leading-5 text-[#a3a3a3]">
|
||||||
Evolução de absenteísmo e risco da semana
|
Evolução de absenteísmo e risco da semana
|
||||||
</p>
|
</p>
|
||||||
@@ -65,8 +76,11 @@ export function HomePage({ navigate }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid gap-4">
|
<div className="grid gap-4">
|
||||||
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5">
|
<div className={`rounded-2xl border bg-[#262626] p-5 ${featurePanelClass('mock')}`}>
|
||||||
<h2 className="text-base font-bold text-[#e5e5e5]">Pacientes de hoje</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold text-[#e5e5e5]">Pacientes de hoje</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<div className="mt-4 grid gap-3">
|
<div className="mt-4 grid gap-3">
|
||||||
{appointmentsToday.map((item) => (
|
{appointmentsToday.map((item) => (
|
||||||
<button
|
<button
|
||||||
@@ -85,8 +99,11 @@ export function HomePage({ navigate }) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="rounded-2xl border border-[#404040] bg-[#262626] p-5">
|
<div className={`rounded-2xl border bg-[#262626] p-5 ${featurePanelClass('mock')}`}>
|
||||||
<h2 className="text-base font-bold text-[#e5e5e5]">Alerta preditivo</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold text-[#e5e5e5]">Alerta preditivo</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<p className="mt-3 text-sm leading-6 text-[#a3a3a3]">
|
<p className="mt-3 text-sm leading-6 text-[#a3a3a3]">
|
||||||
3 pacientes apresentam risco de falta. Recomenda-se confirmar presença antes das 16h.
|
3 pacientes apresentam risco de falta. Recomenda-se confirmar presença antes das 16h.
|
||||||
</p>
|
</p>
|
||||||
@@ -102,7 +119,10 @@ export function HomePage({ navigate }) {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="grid gap-4" id="relatorios">
|
<section className="grid gap-4" id="relatorios">
|
||||||
<h2 className="text-base font-bold text-[#e5e5e5]">Relatórios e Análises</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-base font-bold text-[#e5e5e5]">Relatórios e Análises</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<div className="grid gap-4 lg:grid-cols-2">
|
<div className="grid gap-4 lg:grid-cols-2">
|
||||||
<button
|
<button
|
||||||
className="relative min-h-[164px] overflow-hidden rounded-2xl border border-[#3b82f6] bg-[#262626] p-5 text-left shadow-[0_1px_3px_rgba(0,0,0,0.2)]"
|
className="relative min-h-[164px] overflow-hidden rounded-2xl border border-[#3b82f6] bg-[#262626] p-5 text-left shadow-[0_1px_3px_rgba(0,0,0,0.2)]"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
import { medicalRecordRepository } from '../repositories/medicalRecordRepository.js'
|
import { medicalRecordRepository } from '../repositories/medicalRecordRepository.js'
|
||||||
|
|
||||||
|
|
||||||
@@ -34,6 +35,12 @@ export function MedicalRecordsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6 text-[#e5e5e5]">
|
<div className="mx-auto max-w-7xl space-y-6 text-[#e5e5e5]">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Prontuário, listagem e criação de registros ainda usam dados locais e não persistem na API."
|
||||||
|
status="mock"
|
||||||
|
title="Prontuário ainda é mockado"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#e5e5e5]">Prontuário Médico</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#e5e5e5]">Prontuário Médico</h1>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { communicationRepository } from '../repositories/communicationRepository.js'
|
import { communicationRepository } from '../repositories/communicationRepository.js'
|
||||||
|
|
||||||
const channels = {
|
const channels = {
|
||||||
@@ -155,6 +157,12 @@ export function MessagesPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6">
|
<div className="mx-auto max-w-7xl space-y-6">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Envio de SMS usa API. Histórico, templates e campanhas ainda são dados locais de demonstração."
|
||||||
|
status="partial"
|
||||||
|
title="Mensageria híbrida"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Comunicação</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Comunicação</h1>
|
||||||
@@ -210,7 +218,7 @@ export function MessagesPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{activeTab === 'historico' ? (
|
{activeTab === 'historico' ? (
|
||||||
<section className={`${cardClass} p-5 md:p-6`} aria-label="Histórico de comunicação">
|
<section className={`${cardClass} ${featurePanelClass('mock')} p-5 md:p-6`} aria-label="Histórico de comunicação">
|
||||||
<div className="mb-6 flex flex-col gap-3 md:flex-row">
|
<div className="mb-6 flex flex-col gap-3 md:flex-row">
|
||||||
<label className="relative flex-1">
|
<label className="relative flex-1">
|
||||||
<span className="sr-only">Buscar comunicação</span>
|
<span className="sr-only">Buscar comunicação</span>
|
||||||
@@ -279,7 +287,7 @@ export function MessagesPage() {
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{activeTab === 'templates' ? (
|
{activeTab === 'templates' ? (
|
||||||
<section className="space-y-4" aria-label="Templates de comunicação">
|
<section className={`space-y-4 rounded-2xl p-4 ${featurePanelClass('mock')}`} aria-label="Templates de comunicação">
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<button
|
<button
|
||||||
className="inline-flex h-10 items-center gap-2 rounded-sm bg-[#3b82f6] px-4 text-sm font-semibold text-white transition hover:bg-[#2563eb]"
|
className="inline-flex h-10 items-center gap-2 rounded-sm bg-[#3b82f6] px-4 text-sm font-semibold text-white transition hover:bg-[#2563eb]"
|
||||||
@@ -300,7 +308,7 @@ export function MessagesPage() {
|
|||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{activeTab === 'campanha' ? (
|
{activeTab === 'campanha' ? (
|
||||||
<section className={`${cardClass} p-6`} aria-label="Campanhas inteligentes">
|
<section className={`${cardClass} ${featurePanelClass('mock')} p-6`} aria-label="Campanhas inteligentes">
|
||||||
<div className="py-8 text-center">
|
<div className="py-8 text-center">
|
||||||
<div className="mx-auto mb-4 grid size-16 place-items-center rounded-full bg-[#303030]">
|
<div className="mx-auto mb-4 grid size-16 place-items-center rounded-full bg-[#303030]">
|
||||||
<CommIcon className="size-8 text-[#51a2ff]" name="send" />
|
<CommIcon className="size-8 text-[#51a2ff]" name="send" />
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useRef, useState, useEffect } from 'react'
|
import { useRef, useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { profileRepository } from '../repositories/profileRepository.js'
|
import { profileRepository } from '../repositories/profileRepository.js'
|
||||||
import { authRepository } from '../repositories/authRepository.js'
|
import { authRepository } from '../repositories/authRepository.js'
|
||||||
|
|
||||||
@@ -59,13 +61,19 @@ export function ProfilePage({ navigate }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-6xl space-y-6">
|
<div className="mx-auto max-w-6xl space-y-6">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Carregar perfil, avatar e logout usam integração. O botão de salvar preferências desta tela ainda grava só localmente."
|
||||||
|
status="partial"
|
||||||
|
title="Perfil com persistência parcial"
|
||||||
|
/>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Perfil</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Perfil</h1>
|
||||||
<p className="mt-1 text-sm text-[#b8b8b8]">Dados locais do usuário logado e preferências básicas do shell.</p>
|
<p className="mt-1 text-sm text-[#b8b8b8]">Dados locais do usuário logado e preferências básicas do shell.</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="grid gap-6 lg:grid-cols-[1fr_360px]">
|
<div className="grid gap-6 lg:grid-cols-[1fr_360px]">
|
||||||
<section className={`${cardClass} p-6`}>
|
<section className={`${cardClass} ${featurePanelClass('partial')} p-6`}>
|
||||||
<div className="mb-6 flex items-center gap-4">
|
<div className="mb-6 flex items-center gap-4">
|
||||||
{profile.avatarUrl ? (
|
{profile.avatarUrl ? (
|
||||||
<img
|
<img
|
||||||
@@ -134,12 +142,12 @@ export function ProfilePage({ navigate }) {
|
|||||||
<button className="h-10 rounded-sm bg-[#3b82f6] px-4 text-sm font-semibold text-white" type="submit">
|
<button className="h-10 rounded-sm bg-[#3b82f6] px-4 text-sm font-semibold text-white" type="submit">
|
||||||
Salvar alterações
|
Salvar alterações
|
||||||
</button>
|
</button>
|
||||||
{saved ? <span className="rounded bg-emerald-500/20 px-2.5 py-1 text-xs font-bold text-emerald-400">Preferências salvas localmente</span> : null}
|
{saved ? <span className="rounded bg-amber-500/20 px-2.5 py-1 text-xs font-bold text-amber-300">Preferências salvas localmente</span> : null}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<aside className={`${cardClass} p-6`}>
|
<aside className={`${cardClass} ${featurePanelClass('live')} p-6`}>
|
||||||
<h2 className="text-xl font-bold text-[#f5f5f5]">Resumo de acesso</h2>
|
<h2 className="text-xl font-bold text-[#f5f5f5]">Resumo de acesso</h2>
|
||||||
<dl className="mt-5 grid gap-4 text-sm">
|
<dl className="mt-5 grid gap-4 text-sm">
|
||||||
<Info label="Perfil" value={profile.role} />
|
<Info label="Perfil" value={profile.role} />
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureBadge, FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { reportRepository } from '../repositories/reportRepository.js'
|
import { reportRepository } from '../repositories/reportRepository.js'
|
||||||
import { patientRepository } from '../repositories/patientRepository.js'
|
import { patientRepository } from '../repositories/patientRepository.js'
|
||||||
|
|
||||||
@@ -186,18 +188,25 @@ export function ReportsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6 text-[#e5e5e5]">
|
<div className="mx-auto max-w-7xl space-y-6 text-[#e5e5e5]">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Listagem e salvar laudo usam API. Templates, protocolo de entrega e parte do fluxo editorial ainda são locais."
|
||||||
|
status="partial"
|
||||||
|
title="Laudos com integração parcial"
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<div className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#e5e5e5]">Gestão de Laudos</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#e5e5e5]">Gestão de Laudos</h1>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
|
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
|
||||||
<button
|
<button
|
||||||
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg border border-[#404040] bg-[#262626] px-4 text-sm font-medium text-[#e5e5e5] transition hover:bg-[#2a2a2a]"
|
className={`inline-flex h-10 items-center justify-center gap-2 rounded-lg border px-4 text-sm font-medium text-[#e5e5e5] transition hover:bg-[#2a2a2a] ${featurePanelClass('mock')}`}
|
||||||
onClick={() => setTemplatesOpen(true)}
|
onClick={() => setTemplatesOpen(true)}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<ReportIcon className="size-4 text-[#3b82f6]" name="template" />
|
<ReportIcon className="size-4 text-[#3b82f6]" name="template" />
|
||||||
Templates
|
Templates
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg bg-[#3b82f6] px-4 text-sm font-medium text-white transition hover:bg-[#2563eb]"
|
className="inline-flex h-10 items-center justify-center gap-2 rounded-lg bg-[#3b82f6] px-4 text-sm font-medium text-white transition hover:bg-[#2563eb]"
|
||||||
@@ -219,7 +228,7 @@ export function ReportsPage() {
|
|||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={`${cardClass} p-6`}>
|
<section className={`${cardClass} ${featurePanelClass('live')} p-6`}>
|
||||||
<div className="mb-6 flex flex-col gap-4 md:flex-row">
|
<div className="mb-6 flex flex-col gap-4 md:flex-row">
|
||||||
<div className="relative flex-1">
|
<div className="relative flex-1">
|
||||||
<ReportIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-[#a3a3a3]" name="search" />
|
<ReportIcon className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-[#a3a3a3]" name="search" />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
import { settingsRepository } from '../repositories/settingsRepository.js'
|
import { settingsRepository } from '../repositories/settingsRepository.js'
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +15,13 @@ export function SettingsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-5xl">
|
<div className="mx-auto max-w-5xl">
|
||||||
|
<FeatureCallout
|
||||||
|
className="mb-6"
|
||||||
|
description="Preferências, integrações e backup ainda são protótipos locais, sem persistência real."
|
||||||
|
status="mock"
|
||||||
|
title="Configurações ainda estão em modo protótipo"
|
||||||
|
/>
|
||||||
|
|
||||||
<header className="mb-8">
|
<header className="mb-8">
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Configurações</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Configurações</h1>
|
||||||
<p className="mt-1 text-sm text-[#b8b8b8]">Gerencie preferências, segurança e integrações do MediConnect</p>
|
<p className="mt-1 text-sm text-[#b8b8b8]">Gerencie preferências, segurança e integrações do MediConnect</p>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
import { FeatureBadge, FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
|
import { featurePanelClass } from '../components/featureStateStyles.js'
|
||||||
import { professionalRepository } from '../repositories/professionalRepository.js'
|
import { professionalRepository } from '../repositories/professionalRepository.js'
|
||||||
|
|
||||||
const cardClass = 'rounded-2xl border border-[#404040] bg-[#262626] shadow-sm'
|
const cardClass = 'rounded-2xl border border-[#404040] bg-[#262626] shadow-sm'
|
||||||
@@ -13,6 +15,12 @@ export function TeamPage({ navigate }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6">
|
<div className="mx-auto max-w-7xl space-y-6">
|
||||||
|
<FeatureCallout
|
||||||
|
description="A listagem de profissionais usa API, mas o mapa de cobertura e parte da disponibilidade ainda são simulados."
|
||||||
|
status="partial"
|
||||||
|
title="Tela híbrida: parte real, parte mockada"
|
||||||
|
/>
|
||||||
|
|
||||||
<header className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<header className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Profissionais</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Profissionais</h1>
|
||||||
@@ -29,7 +37,7 @@ export function TeamPage({ navigate }) {
|
|||||||
|
|
||||||
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4" aria-label="Equipe médica">
|
<section className="grid gap-4 md:grid-cols-2 xl:grid-cols-4" aria-label="Equipe médica">
|
||||||
{professionals.map((professional) => (
|
{professionals.map((professional) => (
|
||||||
<article className={`${cardClass} p-5`} key={professional.id}>
|
<article className={`${cardClass} ${featurePanelClass('live')} p-5`} key={professional.id}>
|
||||||
<div className="flex items-start justify-between gap-3">
|
<div className="flex items-start justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="grid size-11 place-items-center rounded-full border border-[#3b82f6]/30 bg-[#3b82f6]/10 text-sm font-bold text-[#3b82f6]">
|
<div className="grid size-11 place-items-center rounded-full border border-[#3b82f6]/30 bg-[#3b82f6]/10 text-sm font-bold text-[#3b82f6]">
|
||||||
@@ -50,10 +58,13 @@ export function TeamPage({ navigate }) {
|
|||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={`${cardClass} p-5`}>
|
<section className={`${cardClass} ${featurePanelClass('mock')} p-5`}>
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-bold text-[#f5f5f5]">Mapa de cobertura</h2>
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<h2 className="text-xl font-bold text-[#f5f5f5]">Mapa de cobertura</h2>
|
||||||
|
<FeatureBadge status="mock" />
|
||||||
|
</div>
|
||||||
<p className="mt-1 text-sm text-[#a3a3a3]">
|
<p className="mt-1 text-sm text-[#a3a3a3]">
|
||||||
Matriz simples para preparar o fluxo de agenda, plantão e disponibilidade.
|
Matriz simples para preparar o fluxo de agenda, plantão e disponibilidade.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useMemo, useState } from 'react'
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { FeatureCallout } from '../components/FeatureState.jsx'
|
||||||
import { visitRepository } from '../repositories/visitRepository.js'
|
import { visitRepository } from '../repositories/visitRepository.js'
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
@@ -35,6 +36,12 @@ export function VisitsPage({ navigate }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto max-w-7xl space-y-6">
|
<div className="mx-auto max-w-7xl space-y-6">
|
||||||
|
<FeatureCallout
|
||||||
|
description="Fila, etapas e resumo desta tela ainda são inteiramente mockados."
|
||||||
|
status="mock"
|
||||||
|
title="Consultas ainda não usam backend"
|
||||||
|
/>
|
||||||
|
|
||||||
<header className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
<header className="flex flex-col items-start justify-between gap-4 md:flex-row md:items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Consultas</h1>
|
<h1 className="text-2xl font-bold tracking-tight text-[#f5f5f5]">Consultas</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user