Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.md]
max_line_length = off
trim_trailing_whitespace = false
29 changes: 29 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = {
root: true,
env: { browser: true, es2023: true, node: true },
parser: '@typescript-eslint/parser',
parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },
settings: { react: { version: 'detect' } },
plugins: ['react', 'react-hooks', '@typescript-eslint', 'jsx-a11y', 'import'],
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
'prettier'
],
rules: {
'react/react-in-jsx-scope': 'off',
'import/order': [
'warn',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
alphabetize: { order: 'asc', caseInsensitive: true },
'newlines-between': 'always'
}
]
}
};
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
dist
.DS_Store
*.log
coverage
*.tsbuildinfo
7 changes: 7 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"tabWidth": 2
}
8 changes: 8 additions & 0 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { StorybookConfig } from '@storybook/react-vite';

const config: StorybookConfig = {
framework: '@storybook/react-vite',
stories: ['../src/**/*.stories.@(ts|tsx)'],
addons: ['@storybook/addon-essentials'],
};
export default config;
21 changes: 21 additions & 0 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import '../src/index.css';
import React from 'react';
import { AuthProvider } from '../src/contexts/AuthContext';
import type { Preview } from '@storybook/react';
import { MemoryRouter } from 'react-router-dom';

const preview: Preview = {
decorators: [
(Story) => (
<MemoryRouter initialEntries={['/notificacoes']}>
<AuthProvider>
<div className="bg-background min-h-screen p-4 text-preto">
<Story />
</div>
</AuthProvider>
</MemoryRouter>
),
],
};

export default preview;
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Mia Ajuda

Projeto React + TypeScript inicializado com Vite, Tailwind CSS e React Router seguindo boas práticas.

## Tecnologias

- React 18
- TypeScript 5
- Vite 5
- Tailwind CSS 3
- React Router 6
- ESLint + Prettier
- Vitest

## Scripts

- `yarn dev` – Ambiente de desenvolvimento
- `yarn build` – Build de produção
- `yarn preview` – Servir build local
- `yarn lint` – Lint
- `yarn format` – Formatador
- `yarn typecheck` – Checagem de tipos
- `yarn test` – Testes

## Estrutura

```
src/
main.tsx // Entrada
routes/ // Definições de rotas
ui/ // Componentes e páginas
utils/ // Utilidades
```

## Tailwind Tokens

Configurados em `tailwind.config.cjs`:

```
colors: primaria (#4B8AB9), preto (#353535), background (#F2F2F7)
```

## Próximos Passos Sugeridos

- Implementar autenticação real (OAuth / email) e estado global
- Adicionar testes e2e (Playwright ou Cypress)
- Configurar CI (GitHub Actions)
- Adicionar rota protegida

## Como iniciar

```bash
yarn install
yarn dev
```

Abra http://localhost:5173.
17 changes: 17 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Material Icons font -->
<link
href="https://fonts.googleapis.com/icon?family=Material+Icons"
rel="stylesheet"
/>
<title>Mia Ajuda</title>
</head>
<body class="bg-background text-preto font-sans">
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
53 changes: 53 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "mia-ajuda",
"private": true,
"version": "0.1.0",
"type": "module",
"packageManager": "yarn@1.22.22",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview",
"lint": "eslint . --ext .ts,.tsx --max-warnings=0",
"format": "prettier --write .",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"test:watch": "vitest",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.23.0",
"react-icons": "^5.2.1",
"react-hook-form": "^7.53.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.0",
"@testing-library/react": "^14.3.0",
"@types/react": "^18.2.21",
"@types/react-dom": "^18.2.7",
"@types/node": "^20.14.10",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"@vitejs/plugin-react": "^4.2.0",
"@storybook/react": "^8.1.0",
"@storybook/react-vite": "^8.1.0",
"@storybook/addon-essentials": "^8.1.0",
"storybook": "^8.1.0",
"autoprefixer": "^10.4.20",
"eslint": "^9.2.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-jsx-a11y": "^6.8.0",
"eslint-plugin-react": "^7.34.0",
"eslint-plugin-react-hooks": "^4.6.0",
"postcss": "^8.4.38",
"prettier": "^3.3.2",
"tailwindcss": "^3.4.7",
"typescript": "^5.5.4",
"vite": "^5.2.0",
"vitest": "^1.6.0"
}
}
6 changes: 6 additions & 0 deletions postcss.config.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
1 change: 1 addition & 0 deletions src/assets/welcome/intro1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/welcome/intro2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/welcome/intro3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/assets/welcome/intro4.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added src/assets/welcome/slide1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added src/assets/welcome/slide2.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added src/assets/welcome/slide3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { createContext, useContext, useState, ReactNode, useCallback } from 'react';

export interface User {
id: string;
name: string;
email: string;
avatarUrl?: string;
}

interface AuthContextValue {
user: User | null;
login: (user: User) => void;
logout: () => void;
}

const AuthContext = createContext<AuthContextValue | undefined>(undefined);

export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>({
id: '1',
name: 'Ana Maria Silva',
email: 'ana.maria@email.com',
avatarUrl: undefined,
});

const login = useCallback((u: User) => setUser(u), []);
const logout = useCallback(() => setUser(null), []);

return (
<AuthContext.Provider value={{ user, login, logout }}>{children}</AuthContext.Provider>
);
}

export function useAuth() {
const ctx = useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
return ctx;
}
11 changes: 11 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
--color-primaria: #4b8ab9;
--color-preto: #353535;
--color-background: #f2f2f7;
}

body { @apply bg-background text-preto; }
17 changes: 17 additions & 0 deletions src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';

import './index.css';
import { AppRoutes } from './routes/AppRoutes';
import { AuthProvider } from './contexts/AuthContext';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<AuthProvider>
<AppRoutes />
</AuthProvider>
</BrowserRouter>
</React.StrictMode>
);
34 changes: 34 additions & 0 deletions src/routes/AppRoutes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Route, Routes } from 'react-router-dom';

import { RootLayout } from '../ui/layouts/RootLayout';
import { HomePage } from '../ui/pages/HomePage';
import { LoginPage } from '../ui/pages/LoginPage';
import { NotFoundPage } from '../ui/pages/NotFoundPage';
import { NotificationsPage } from '../ui/pages/NotificationsPage';
import { MapPage } from '../ui/pages/MapPage';
import { OrdersPage } from '../ui/pages/OrdersPage';
import { TimelinePage } from '../ui/pages/TimelinePage';
import { FeedbacksPage } from '../ui/pages/FeedbacksPage';
import { ProfilePage } from '../ui/pages/ProfilePage';
import { HelpPage } from '../ui/pages/HelpPage';
import { RegisterWizard } from '../ui/pages/register/RegisterWizard';

export function AppRoutes() {
return (
<Routes>
<Route path="/" element={<RootLayout />}>
<Route index element={<HomePage />} />
<Route path="login" element={<LoginPage />} />
<Route path="notificacoes" element={<NotificationsPage />} />
<Route path="mapa" element={<MapPage />} />
<Route path="pedidos" element={<OrdersPage />} />
<Route path="timeline" element={<TimelinePage />} />
<Route path="feedbacks" element={<FeedbacksPage />} />
<Route path="perfil" element={<ProfilePage />} />
<Route path="ajuda" element={<HelpPage />} />
<Route path="register" element={<RegisterWizard />} />
<Route path="*" element={<NotFoundPage />} />
</Route>
</Routes>
);
}
4 changes: 4 additions & 0 deletions src/types/storybook.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Temporary module declarations (can be removed once types are resolved by dependency install)
declare module '@storybook/react';
declare module '@storybook/react-vite';
declare module '@storybook/addon-essentials';
4 changes: 4 additions & 0 deletions src/types/svg.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare module '*.svg' {
const src: string;
export default src;
}
32 changes: 32 additions & 0 deletions src/ui/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ButtonHTMLAttributes, forwardRef } from 'react';
import { cn } from '../../utils/cn';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost';
loading?: boolean;
}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant = 'primary', loading, disabled, children, ...rest }, ref) => {
const base = 'inline-flex items-center justify-center rounded-lg text-sm font-semibold tracking-wide uppercase focus:outline-none focus-visible:ring-4 focus-visible:ring-primaria/20 transition-colors h-11 px-6 shadow-sm';
const variants: Record<string, string> = {
primary: 'bg-primaria text-white hover:bg-primaria/90 focus-visible:ring-primaria disabled:bg-primaria/50',
secondary: 'bg-white text-preto border border-preto/10 hover:bg-preto/5 focus-visible:ring-primaria',
ghost: 'bg-transparent hover:bg-preto/5 text-preto focus-visible:ring-primaria'
};
return (
<button
ref={ref}
className={cn(base, variants[variant], className)}
disabled={disabled || loading}
{...rest}
>
{loading && (
<span className="mr-2 inline-block size-4 animate-spin rounded-full border-2 border-white border-t-transparent" />
)}
{children}
</button>
);
}
);
Button.displayName = 'Button';
7 changes: 7 additions & 0 deletions src/ui/components/Logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function Logo() {
return (
<div className="w-8 h-8 rounded-md bg-primaria flex items-center justify-center text-white font-bold">
M
</div>
);
}
Loading