@alphatechma/expo-rn-template 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.eslintrc.js ADDED
@@ -0,0 +1,4 @@
1
+ module.exports = {
2
+ root: true,
3
+ extends: ['universe/native'],
4
+ };
@@ -0,0 +1,40 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ publish:
11
+ name: Publish package
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Checkout repository
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Use Node.js
18
+ uses: actions/setup-node@v4
19
+ with:
20
+ node-version: '18'
21
+ registry-url: 'https://registry.npmjs.org'
22
+
23
+ - name: Install dependencies
24
+ run: npm ci
25
+
26
+ - name: Run lint
27
+ run: npm run lint || true
28
+
29
+ - name: Run TypeScript check
30
+ run: npx tsc --noEmit || true
31
+
32
+ - name: Build (if defined)
33
+ run: npm run build --if-present
34
+
35
+ - name: Publish to npm
36
+ env:
37
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
38
+ run: |
39
+ echo "Publishing package..."
40
+ npm publish --access public
@@ -0,0 +1,28 @@
1
+ name: Release (semantic-release)
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Checkout
13
+ uses: actions/checkout@v4
14
+
15
+ - name: Setup Node
16
+ uses: actions/setup-node@v4
17
+ with:
18
+ node-version: '18'
19
+ registry-url: 'https://registry.npmjs.org'
20
+
21
+ - name: Install dependencies
22
+ run: npm ci
23
+
24
+ - name: Run semantic-release
25
+ env:
26
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
27
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
28
+ run: npx semantic-release
@@ -0,0 +1,5 @@
1
+ #!/bin/sh
2
+ . "$(dirname "$0")/_/husky.sh"
3
+
4
+ # Run lint-staged on staged files
5
+ npx lint-staged
@@ -0,0 +1,11 @@
1
+ {
2
+ "branches": ["main"],
3
+ "repositoryUrl": "https://github.com/alphatechma/expo-rn-template-alphatech",
4
+ "plugins": [
5
+ ["@semantic-release/commit-analyzer", { "preset": "angular" }],
6
+ "@semantic-release/release-notes-generator",
7
+ "@semantic-release/changelog",
8
+ ["@semantic-release/npm", { "npmPublish": true }],
9
+ ["@semantic-release/git", { "assets": ["package.json", "CHANGELOG.md"], "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" }]
10
+ ]
11
+ }
package/App.tsx ADDED
@@ -0,0 +1,49 @@
1
+ import {
2
+ Roboto_300Light,
3
+ Roboto_400Regular,
4
+ Roboto_500Medium,
5
+ Roboto_700Bold,
6
+ Roboto_900Black,
7
+ useFonts,
8
+ } from '@expo-google-fonts/roboto';
9
+ import Routes from '@routes/index';
10
+ import {setupStore} from '@src/store/store';
11
+ import {StatusBar} from 'expo-status-bar';
12
+ import React from 'react';
13
+ import {ActivityIndicator, View} from 'react-native';
14
+ import {GestureHandlerRootView} from 'react-native-gesture-handler';
15
+ import {Provider} from 'react-redux';
16
+
17
+ import AppProvider from './src/hooks';
18
+
19
+ export default function App() {
20
+ const [fontsLoaded] = useFonts({
21
+ Roboto_300Light,
22
+ Roboto_400Regular,
23
+ Roboto_500Medium,
24
+ Roboto_700Bold,
25
+ Roboto_900Black,
26
+ });
27
+
28
+ if (!fontsLoaded) {
29
+ return (
30
+ <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
31
+ <ActivityIndicator color="aliceblue"/>
32
+ </View>
33
+ );
34
+ }
35
+ const store = setupStore();
36
+
37
+ return (
38
+ <GestureHandlerRootView style={{flex: 1}}>
39
+ <Provider store={store}>
40
+ <StatusBar style="light"/>
41
+ <AppProvider>
42
+ <View style={{flex: 1}}>
43
+ <Routes/>
44
+ </View>
45
+ </AppProvider>
46
+ </Provider>
47
+ </GestureHandlerRootView>
48
+ );
49
+ }
package/README.md ADDED
@@ -0,0 +1,259 @@
1
+ # RNTemplate — Template Expo + React Native
2
+
3
+ Um template mínimo para iniciar apps Expo + React Native com convenções já preparadas:
4
+ - TypeScript configurado (via `tsconfig.json`)
5
+ - Navegação (React Navigation) com Stack + Bottom Tabs
6
+ - Estrutura de pastas (páginas, features, estilos, routes, store)
7
+ - ESLint + Prettier configurados (Husky + lint-staged incluídos para pre-commit)
8
+
9
+ Este README explica como usar e personalizar o template rapidamente.
10
+
11
+ ---
12
+
13
+ ## Sumário
14
+ - Quickstart
15
+ - Scripts úteis
16
+ - Lint / Prettier / Husky
17
+ - Estrutura do projeto e como adicionar páginas/rotas
18
+ - Store / API (RTK Query)
19
+ - Tipos TypeScript e módulos sem tipos
20
+ - Troubleshooting comuns
21
+ - Próximos passos e personalizações sugeridas
22
+ - Publicação no npm (via GitHub Actions)
23
+
24
+ ---
25
+
26
+ ## Quickstart
27
+
28
+ 1) Instale dependências
29
+
30
+ ```bash
31
+ npm install --legacy-peer-deps
32
+ ```
33
+
34
+ Se você usa `yarn` ou `pnpm` adapte o comando conforme a sua preferência.
35
+
36
+ 2) Instale os hooks do Husky (preparar git hooks)
37
+
38
+ ```bash
39
+ npm run prepare
40
+ # ou (manual)
41
+ npx husky install
42
+ ```
43
+
44
+ 3) Rodar o projeto (Expo)
45
+
46
+ ```bash
47
+ npm run start
48
+ # abrir no iOS / Android / Web
49
+ npm run ios
50
+ npm run android
51
+ npm run web
52
+ ```
53
+
54
+ 4) Validar TypeScript e formatar código
55
+
56
+ ```bash
57
+ # checar tipos (sem emitir build)
58
+ npx tsc --noEmit
59
+
60
+ # formatar código (Prettier)
61
+ npm run format
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Scripts importantes (package.json)
67
+ - `npm run start` — inicia o Metro/Expo
68
+ - `npm run ios` / `npm run android` / `npm run web` — abre para a plataforma correspondente
69
+ - `npm run lint` — executa ESLint (configável)
70
+ - `npm run lint:fix` — aplica correções automáticas onde possível
71
+ - `npm run format` — executa Prettier
72
+ - `npm run prepare` — instala os hooks do Husky
73
+
74
+ ---
75
+
76
+ ## Lint / Prettier / Husky
77
+
78
+ O template já contém configuração mínima:
79
+ - `.eslintrc.js` com `extends: ['universe/native']` (sua preferência de config)
80
+ - `eslint.config.cjs` neutralizado para evitar conflito com flat config
81
+ - `.prettierrc` e `.prettierignore` prontos
82
+ - `husky` + `lint-staged` configurados: pre-commit executa `lint-staged`
83
+
84
+ Se quiser executar os checks localmente:
85
+
86
+ ```bash
87
+ # listar problemas de lint
88
+ npm run lint
89
+
90
+ # tentar corrigir automaticamente
91
+ npm run lint:fix
92
+ ```
93
+
94
+ Se os hooks não estiverem ativos (por exemplo, em CI ou após clonar):
95
+
96
+ ```bash
97
+ npm run prepare
98
+ ```
99
+
100
+ Nota: em ambientes com problemas de peers ou tokens de npm você pode precisar rodar:
101
+
102
+ ```bash
103
+ npm config delete //registry.npmjs.org/:_authToken
104
+ npm login
105
+ # depois
106
+ npm install --legacy-peer-deps
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Estrutura do projeto (visão rápida)
112
+
113
+ Principais pastas:
114
+ - `src/pages/` — telas (ex.: `Home`, `Login`, etc). O template já tem placeholders.
115
+ - `src/routes/` — onde o Router/Navigation vive (`src/routes/index.tsx` já transformado em template)
116
+ - `src/features/` — features domain-driven (ex.: `account`, `api` — RTK Query slices)
117
+ - `src/store/` — configuração do Redux / store
118
+ - `src/styles/` — theme, styled.d.ts e tokens
119
+ - `src/hooks/` — providers / wrappers do app (ThemeProvider, NavigationContainer)
120
+ - `src/@types/` — declarações de módulos e tipos globais
121
+
122
+ Como adicionar uma tela simples e conectá-la nas rotas:
123
+
124
+ 1. Criar arquivo `src/pages/MinhaPagina/index.tsx` com um componente React Native.
125
+ 2. Importar a tela em `src/routes/index.tsx` (ou mover o placeholder para o arquivo novo).
126
+ 3. Adicionar um `Tab.Screen` ou `Stack.Screen` conforme desejar.
127
+
128
+ Exemplo rápido (conceptual):
129
+
130
+ ```tsx
131
+ // src/pages/MinhaPagina/index.tsx
132
+ import React from 'react';
133
+ import { View, Text } from 'react-native';
134
+ export default function MinhaPagina() {
135
+ return (
136
+ <View><Text>Minha página</Text></View>
137
+ );
138
+ }
139
+ ```
140
+
141
+ E em `src/routes/index.tsx` adicione a rota ao `Tab.Navigator`.
142
+
143
+ ---
144
+
145
+ ## Store e API (RTK Query)
146
+
147
+ O template inclui um `apiSlice` pronto para injeção de endpoints e um `setupStore` em `src/store/store.ts`.
148
+ - `src/features/api/apiSlice.ts` — base para RTK Query (configure `baseUrl` e o token se necessário)
149
+ - Para criar endpoints de exemplo, veja o `src/features/account/exampleSlice.ts` já incluído como template CRUD usando `apiSlice.injectEndpoints`.
150
+
151
+ Para usar o store em sua aplicação, configure o provider no `App.tsx` (ou em `src/hooks`) e passe o `store`:
152
+
153
+ ```tsx
154
+ import { Provider } from 'react-redux';
155
+ import { setupStore } from './src/store/store';
156
+ const store = setupStore();
157
+
158
+ // ... wrap your App with <Provider store={store}> ...
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Tipos TypeScript e módulos sem tipos
164
+
165
+ Alguns pacotes não fornecem typings. Para evitar erros do TypeScript o template inclui `src/@types/index.d.ts` com declarações genéricas para imagens e alguns pacotes. Recomenda-se instalar tipos oficiais quando existirem:
166
+
167
+ ```bash
168
+ npm i -D @types/react @types/react-native
169
+ ```
170
+
171
+ Se você adicionar uma dependência que não tem tipos, adicione uma declaração minimal em `src/@types/index.d.ts` como:
172
+
173
+ ```ts
174
+ declare module 'nome-do-pacote';
175
+ ```
176
+
177
+ ---
178
+
179
+ ## Troubleshooting comum
180
+
181
+ - Erro ao instalar pacotes: `ERESOLVE` ou conflito de peer
182
+ - Tente: `npm install --legacy-peer-deps`
183
+ - Erro `Access token expired or revoked` no `npm install`
184
+ - Refaça login: `npm logout` e `npm login` ou delete o token local:
185
+
186
+ ```bash
187
+ npm config delete //registry.npmjs.org/:_authToken
188
+ npm login
189
+ ```
190
+
191
+ - Erro do TypeScript sobre `.tsx` / `--jsx is not set`
192
+ - Solução: `tsconfig.json` já tem `"jsx": "react-jsx"` configurado. Se estiver faltando, adicione.
193
+
194
+ - Regras do ESLint/Flat config conflitando
195
+ - O template inclui `.eslintrc.js` (legacy) e `eslint.config.cjs` neutralizado. Se preferir usar a config flat, remova `.eslintrc.js` e mantenha `eslint.config.cjs`.
196
+
197
+ ---
198
+
199
+ ## Publicação no npm (via GitHub Actions)
200
+
201
+ Este template já tem duas opções para publicar no npm a partir do repositório:
202
+
203
+ 1) Publicação por tag (manual)
204
+ - Criar uma tag com formato `vX.Y.Z` e dar push. Exemplo local:
205
+
206
+ ```bash
207
+ npm run release -- 1.0.2
208
+ # isso executa: git tag -a v1.0.2 -m "Release v1.0.2" && git push origin v1.0.2
209
+ ```
210
+
211
+ - O workflow `.github/workflows/publish.yml` será disparado no push da tag e executará (instalação, lint, tsc, build, publish).
212
+
213
+ 2) Publicação automática com `semantic-release` (recomendada para releases automáticas)
214
+ - Push para a branch `main` acionará `.github/workflows/release.yml` que executa `semantic-release`.
215
+ - `semantic-release` analisa commits (conventional commits) e gera a nova versão, atualiza `CHANGELOG.md`, faz `npm publish` e cria uma release no GitHub.
216
+
217
+ ### Requisitos / configurações
218
+ - Adicione o secret `NPM_TOKEN` no repositório (Settings → Secrets and variables → Actions)
219
+ - Nome: `NPM_TOKEN`
220
+ - Valor: token de automação do npm (crie em https://www.npmjs.com/settings/tokens)
221
+ - `GITHUB_TOKEN` é provido automaticamente pelo GitHub Actions; não é necessário configurá-lo manualmente.
222
+
223
+ ### Qual usar?
224
+ - Se você prefere controlar as versões manualmente (tagging), use a opção (1).
225
+ - Se quer CI/CD para releases automáticas com base em commits (convencional), use (2) e siga o padrão `conventional commits` (feat, fix, perf, chore, etc.).
226
+
227
+ ---
228
+
229
+ ## Próximos passos e personalizações sugeridas
230
+
231
+ - Defina `baseUrl` da API em `src/features/api/apiSlice.ts` e configure as rotas reais.
232
+ - Substitua placeholders em `src/pages/*` por componentes reais e desça a árvore.
233
+ - Adicione/atualize tokens e temas em `src/styles/theme.ts`.
234
+ - Ajuste regras do ESLint conforme a sua equipe (ex.: permitir `any` em alguns casos, strenghten rules, etc).
235
+ - Considere adicionar GitHub Actions para rodar `npm run lint` e `npx tsc --noEmit` no CI.
236
+
237
+ ---
238
+
239
+ ## Arquivos importantes para revisão
240
+ - `package.json` — scripts, dependências e hooks
241
+ - `tsconfig.json` — paths e jsx
242
+ - `src/routes/index.tsx` — ponto central de rotas (já convertido para template)
243
+ - `src/features/api/apiSlice.ts` — base para chamadas HTTP (RTK Query)
244
+ - `src/store/store.ts` — setup do Redux
245
+ - `src/@types/index.d.ts` — declare módulos sem tipos
246
+
247
+ ---
248
+
249
+ ## Licença
250
+ Coloque aqui a licença do seu template (ex.: MIT) se for público.
251
+
252
+ ---
253
+
254
+ Se quiser eu:
255
+ - 1) adapto esse README para uma versão curta em inglês;
256
+ - 2) gero instruções de CI (GitHub Actions) que rodem `npm install`, `npm run lint` e `npx tsc --noEmit`;
257
+ - 3) adiciono um script `create-app` simples que copia o template e roda um assistant de setup.
258
+
259
+ Diga qual das opções acima prefere que eu crie em seguida.
package/app.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "expo": {
3
+ "name": "RNTemplate",
4
+ "slug": "RNTemplate",
5
+ "version": "1.0.0",
6
+ "orientation": "portrait",
7
+ "icon": "./assets/icon.png",
8
+ "userInterfaceStyle": "light",
9
+ "newArchEnabled": true,
10
+ "splash": {
11
+ "image": "./assets/splash-icon.png",
12
+ "resizeMode": "contain",
13
+ "backgroundColor": "#ffffff"
14
+ },
15
+ "ios": {
16
+ "supportsTablet": true
17
+ },
18
+ "android": {
19
+ "adaptiveIcon": {
20
+ "foregroundImage": "./assets/adaptive-icon.png",
21
+ "backgroundColor": "#ffffff"
22
+ },
23
+ "edgeToEdgeEnabled": true,
24
+ "predictiveBackGestureEnabled": false
25
+ },
26
+ "web": {
27
+ "favicon": "./assets/favicon.png"
28
+ }
29
+ }
30
+ }
Binary file
Binary file
Binary file
Binary file
package/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import { registerRootComponent } from 'expo';
2
+
3
+ import App from './App';
4
+
5
+ // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6
+ // It also ensures that whether you load the app in Expo Go or in a native build,
7
+ // the environment is set up appropriately
8
+ registerRootComponent(App);
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@alphatechma/expo-rn-template",
3
+ "version": "1.0.2",
4
+ "main": "index.ts",
5
+ "description": "Expo React Native template by Alphatech - base templatizada para apps Expo.",
6
+ "keywords": ["expo", "react-native", "template", "starter", "typescript", "mobile", "alphatech"],
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/alphatechma/expo-rn-template.git"
10
+ },
11
+ "homepage": "https://github.com/alphatechma/expo-rn-template#readme",
12
+ "scripts": {
13
+ "start": "expo start",
14
+ "android": "expo start --android",
15
+ "ios": "expo start --ios",
16
+ "lint": "eslint --config .eslintrc.js --ext .js,.jsx,.ts,.tsx src",
17
+ "lint:fix": "eslint --config .eslintrc.js --ext .js,.jsx,.ts,.tsx src --fix",
18
+ "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"",
19
+ "prepare": "husky install",
20
+ "release": "bash -lc '\\\n VERSION=$1;\\\n if [ -z \"$VERSION\" ]; then echo \"Usage: npm run release -- <version> (eg. npm run release -- 1.0.0)\"; exit 1; fi;\\\n git tag -a v$VERSION -m \"Release v$VERSION\" && git push origin v$VERSION;\\\n echo \"Tagged v$VERSION and pushed. GitHub Action will publish on push tag.\"\"",
21
+ "semantic-release": "semantic-release"
22
+ },
23
+ "dependencies": {
24
+ "@expo-google-fonts/roboto": "^0.4.2",
25
+ "@expo/metro-runtime": "^6.1.2",
26
+ "@gorhom/bottom-sheet": "^5.2.8",
27
+ "@hookform/resolvers": "^5.2.2",
28
+ "@react-native-async-storage/async-storage": "^2.2.0",
29
+ "@react-native-community/checkbox": "^0.5.20",
30
+ "@react-native-community/datetimepicker": "^8.6.0",
31
+ "@react-native-community/hooks": "^100.1.0",
32
+ "@react-native-masked-view/masked-view": "^0.3.2",
33
+ "@react-native-picker/picker": "^2.11.4",
34
+ "@react-navigation/bottom-tabs": "^7.9.1",
35
+ "@react-navigation/native": "^7.1.27",
36
+ "@react-navigation/native-stack": "^7.9.1",
37
+ "@react-navigation/stack": "^7.6.14",
38
+ "@reduxjs/toolkit": "^2.11.2",
39
+ "buffer": "^6.0.3",
40
+ "core-js": "^3.47.0",
41
+ "date-fns": "^4.1.0",
42
+ "expo": "~54.0.31",
43
+ "expo-location": "^19.0.8",
44
+ "expo-splash-screen": "^31.0.13",
45
+ "expo-status-bar": "~3.0.9",
46
+ "expo-updates": "^29.0.16",
47
+ "react": "19.1.0",
48
+ "react-dom": "19.1.0",
49
+ "react-hook-form": "^7.71.1",
50
+ "react-native": "0.81.5",
51
+ "react-native-date-picker": "^5.0.13",
52
+ "react-native-gesture-handler": "^2.30.0",
53
+ "react-native-iphone-x-helper": "^1.3.1",
54
+ "react-native-reanimated": "~3.7.0",
55
+ "react-native-responsive-fontsize": "^0.5.1",
56
+ "react-native-safe-area-context": "^5.6.2",
57
+ "react-native-screens": "^4.19.0",
58
+ "react-native-svg": "^15.15.1",
59
+ "react-redux": "^9.2.0",
60
+ "styled-components": "^6.3.8",
61
+ "yup": "^1.7.1"
62
+ },
63
+ "devDependencies": {
64
+ "@types/react": "^19.2.8",
65
+ "eslint": "^8.52.0",
66
+ "eslint-config-universe": "^12.0.0",
67
+ "prettier": "^3.0.3",
68
+ "husky": "^8.0.0",
69
+ "lint-staged": "^15.0.0",
70
+ "typescript": "^5.3.3",
71
+ "semantic-release": "^21.0.0",
72
+ "@semantic-release/changelog": "^6.0.0",
73
+ "@semantic-release/git": "^10.0.0",
74
+ "@semantic-release/npm": "^9.0.0",
75
+ "@semantic-release/release-notes-generator": "^10.0.0",
76
+ "@semantic-release/commit-analyzer": "^10.0.0"
77
+ },
78
+ "lint-staged": {
79
+ "src/**/*.{js,jsx,ts,tsx}": [
80
+ "npm run lint:fix",
81
+ "npm run format",
82
+ "git add"
83
+ ],
84
+ "src/**/*.{json,css,md}": [
85
+ "npm run format",
86
+ "git add"
87
+ ]
88
+ },
89
+ "publishConfig": {
90
+ "access": "public"
91
+ }
92
+ }
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ singleQuote: true,
3
+ trailingComma: 'all',
4
+ arrowParens: 'avoid',
5
+ };
@@ -0,0 +1,4 @@
1
+ declare module '*.png';
2
+ declare module '*.svg';
3
+ declare module 'expo-keycloak-auth-adapter';
4
+ declare module 'react-native-popup-confirm-toast';
@@ -0,0 +1,5 @@
1
+ export declare global {
2
+ namespace ReactNavigation {
3
+ interface RootParamList {}
4
+ }
5
+ }
File without changes
File without changes
@@ -0,0 +1,28 @@
1
+ import { createContext, ReactNode, useMemo, useState } from 'react';
2
+
3
+ export type LoadingContextData = {
4
+ loading: boolean;
5
+ setLoading: (value: boolean) => void;
6
+ };
7
+
8
+ export const LoadingContext = createContext({} as LoadingContextData);
9
+
10
+ type AuthContextProps = {
11
+ children: ReactNode;
12
+ };
13
+
14
+ export function LoadingProvider({ children }: AuthContextProps) {
15
+ const [loading, setLoading] = useState(false);
16
+ const updatedValue = useMemo(
17
+ () => ({
18
+ loading,
19
+ setLoading,
20
+ }),
21
+ [loading],
22
+ );
23
+ return (
24
+ <LoadingContext.Provider value={updatedValue}>
25
+ {children}
26
+ </LoadingContext.Provider>
27
+ );
28
+ }
@@ -0,0 +1,34 @@
1
+ import AsyncStorage from '@react-native-async-storage/async-storage';
2
+ import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
3
+
4
+ /**
5
+ * Recupera o token armazenado no AsyncStorage pra enviar no cabeçalho Authorization
6
+ * @returns {Promise<string | undefined>} O token de acesso ou undefined se não existir
7
+ * precisa substituir o baseUrl pela URL da sua API, preferencialmente usando variáveis de ambiente
8
+ */
9
+ // Recupera o token armazenado no AsyncStorage pra enviar no cabeçalho Authorization
10
+ export const baseUrl = '';
11
+ // Pode substituir por cookie também se preferir
12
+ const TOKEN = 'nome_do_token_no_async_storage';
13
+ const getToken = async () => {
14
+ const tk = await AsyncStorage.getItem(TOKEN);
15
+ const token = JSON.parse(tk || '{}');
16
+ return token.accessToken;
17
+ };
18
+
19
+ export const apiSlice = createApi({
20
+ reducerPath: 'api',
21
+ // Adicione aqui os TagTypes usados pelos endpoints injetados
22
+ tagTypes: ['Example'],
23
+ endpoints: builder => ({}),
24
+ baseQuery: fetchBaseQuery({
25
+ baseUrl,
26
+ prepareHeaders: async headers => {
27
+ const token = await getToken();
28
+ if (token) {
29
+ headers.set('Authorization', `Bearer ${token}`);
30
+ }
31
+ return headers;
32
+ },
33
+ }),
34
+ });
@@ -0,0 +1,77 @@
1
+ import { apiSlice } from '../api/apiSlice';
2
+
3
+ // Tipos de exemplo - adapte conforme necessário
4
+ export interface Example {
5
+ id: string | number;
6
+ name?: string;
7
+ description?: string;
8
+ [key: string]: any;
9
+ }
10
+
11
+ export type CreateExamplePayload = Partial<Example>;
12
+ export type UpdateExamplePayload = {
13
+ id: string | number;
14
+ data: Partial<Example>;
15
+ };
16
+
17
+ /**
18
+ * Exemplo de API slice com operações CRUD.
19
+ * - getExamples: lista exemplos (GET /examples)
20
+ * - getExampleById: obtém um exemplo por id (GET /examples/:id)
21
+ * - createExample: cria um novo exemplo (POST /examples)
22
+ * - updateExample: atualiza um exemplo existente (PUT /examples/:id)
23
+ * - deleteExample: remove um exemplo (DELETE /examples/:id)
24
+ *
25
+ * Substitua as rotas e os tipos conforme a sua API.
26
+ */
27
+ export const exampleApiSlice = apiSlice.injectEndpoints({
28
+ endpoints: build => ({
29
+ getExamples: build.query<Example[], void>({
30
+ query: () => ({ url: '/examples', method: 'GET' }),
31
+ providesTags: result =>
32
+ result
33
+ ? [
34
+ ...result.map(r => ({ type: 'Example' as const, id: r.id })),
35
+ { type: 'Example', id: 'LIST' },
36
+ ]
37
+ : [{ type: 'Example', id: 'LIST' }],
38
+ }),
39
+
40
+ getExampleById: build.query<Example, string | number>({
41
+ query: id => ({ url: `/examples/${id}`, method: 'GET' }),
42
+ providesTags: (result, error, id) => [{ type: 'Example' as const, id }],
43
+ }),
44
+
45
+ createExample: build.mutation<Example, CreateExamplePayload>({
46
+ query: body => ({ url: '/examples', method: 'POST', body }),
47
+ invalidatesTags: [{ type: 'Example', id: 'LIST' }],
48
+ }),
49
+
50
+ updateExample: build.mutation<Example, UpdateExamplePayload>({
51
+ query: ({ id, data }) => ({
52
+ url: `/examples/${id}`,
53
+ method: 'PUT',
54
+ body: data,
55
+ }),
56
+ invalidatesTags: (result, error, arg) => [
57
+ { type: 'Example', id: arg.id },
58
+ ],
59
+ }),
60
+
61
+ deleteExample: build.mutation<{ success: boolean }, string | number>({
62
+ query: id => ({ url: `/examples/${id}`, method: 'DELETE' }),
63
+ invalidatesTags: (result, error, id) => [{ type: 'Example', id }],
64
+ }),
65
+ }),
66
+ });
67
+
68
+ // Hooks gerados pelo RTK Query para consumo nas telas/componetes
69
+ export const {
70
+ useGetExamplesQuery,
71
+ useGetExampleByIdQuery,
72
+ useCreateExampleMutation,
73
+ useUpdateExampleMutation,
74
+ useDeleteExampleMutation,
75
+ } = exampleApiSlice;
76
+
77
+ export default exampleApiSlice;
@@ -0,0 +1,31 @@
1
+ import { LoadingProvider } from '@contexts/LoadingContext';
2
+ import { NavigationContainer } from '@react-navigation/native';
3
+ import theme from '@src/styles/theme';
4
+ import React, { ReactElement } from 'react';
5
+ import { View } from 'react-native';
6
+ import { GestureHandlerRootView } from 'react-native-gesture-handler';
7
+ import { ThemeProvider } from 'styled-components/native';
8
+
9
+ const PopupRootProvider: any = require('react-native-popup-confirm-toast').Root;
10
+
11
+ interface AppProviderProps {
12
+ children: ReactElement;
13
+ }
14
+
15
+ const AppProvider = ({ children }: AppProviderProps) => {
16
+ return (
17
+ <GestureHandlerRootView style={{ flex: 1 }}>
18
+ <ThemeProvider theme={theme}>
19
+ <PopupRootProvider>
20
+ <LoadingProvider>
21
+ <NavigationContainer>
22
+ <View style={{ flex: 1 }}>{children}</View>
23
+ </NavigationContainer>
24
+ </LoadingProvider>
25
+ </PopupRootProvider>
26
+ </ThemeProvider>
27
+ </GestureHandlerRootView>
28
+ );
29
+ };
30
+
31
+ export default AppProvider;
@@ -0,0 +1,10 @@
1
+ import { LoadingContext } from '@contexts/LoadingContext';
2
+ import { useContext } from 'react';
3
+
4
+ export function useLoading() {
5
+ const context = useContext(LoadingContext);
6
+ if (!context) {
7
+ throw new Error('useLoading must be used within LoadingProvider');
8
+ }
9
+ return context;
10
+ }
@@ -0,0 +1,43 @@
1
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
2
+ import { ParamListBase } from '@react-navigation/native';
3
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
4
+ import { Home } from '@screens/index';
5
+ import React from 'react';
6
+
7
+ // Tipos de navegação (ajuste conforme sua aplicação)
8
+ export type StackParamList = {
9
+ Home: undefined;
10
+ };
11
+
12
+ interface BottomTabRoutes extends ParamListBase {
13
+ Inicio: undefined;
14
+ }
15
+
16
+ const Stack = createNativeStackNavigator<StackParamList>();
17
+ const Tab = createBottomTabNavigator<BottomTabRoutes>();
18
+
19
+ const HomeStackRoutes: React.FC = () => (
20
+ <Stack.Navigator>
21
+ <Stack.Screen
22
+ name="Home"
23
+ component={Home}
24
+ options={{ headerShown: true }}
25
+ />
26
+ </Stack.Navigator>
27
+ );
28
+
29
+ export default function Routes() {
30
+ return (
31
+ <Tab.Navigator
32
+ screenOptions={{
33
+ headerShown: false,
34
+ }}
35
+ >
36
+ <Tab.Screen
37
+ name="Inicio"
38
+ component={HomeStackRoutes}
39
+ options={{ title: 'Início' }}
40
+ />
41
+ </Tab.Navigator>
42
+ );
43
+ }
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { StyleSheet, Text, View } from 'react-native';
3
+
4
+ const Home: React.FC = () => {
5
+ return (
6
+ <View style={styles.container}>
7
+ <Text style={styles.title}>Bem-vindo</Text>
8
+ <Text style={styles.subtitle}>Esta é uma tela template simples.</Text>
9
+ </View>
10
+ );
11
+ };
12
+
13
+ export default Home;
14
+
15
+ const styles = StyleSheet.create({
16
+ container: {
17
+ flex: 1,
18
+ alignItems: 'center',
19
+ justifyContent: 'center',
20
+ padding: 20,
21
+ backgroundColor: '#FFFFFF',
22
+ },
23
+ title: {
24
+ fontSize: 28,
25
+ fontWeight: '700',
26
+ marginBottom: 8,
27
+ color: '#111827',
28
+ },
29
+ subtitle: {
30
+ fontSize: 16,
31
+ color: '#6B7280',
32
+ },
33
+ });
@@ -0,0 +1,85 @@
1
+ import theme from '@theme/theme';
2
+ import { Dimensions } from 'react-native';
3
+ import { getStatusBarHeight } from 'react-native-iphone-x-helper';
4
+ import { RFValue } from 'react-native-responsive-fontsize';
5
+ import styled from 'styled-components/native';
6
+
7
+ export const Container = styled.ScrollView`
8
+ flex: 1;
9
+ background-color: ${({ theme }: any) => theme.colors.white};
10
+ `;
11
+
12
+ export const ContentContainer = styled.View`
13
+ padding: 0 20px 20px;
14
+ `;
15
+
16
+ export const Logo = styled.Image`
17
+ height: ${RFValue(70)}px;
18
+ width: ${RFValue(80)}px;
19
+ `;
20
+
21
+ export const Header = styled.ImageBackground.attrs({
22
+ blurRadius: 5,
23
+ })`
24
+ flex-direction: row;
25
+ height: 160px;
26
+ width: 100%;
27
+ padding-top: ${getStatusBarHeight() + 30}px;
28
+ background-color: ${({ theme }: any) => theme.colors.secondary};
29
+ `;
30
+
31
+ export const HeaderContainer = styled.View.attrs({})`
32
+ flex-direction: row;
33
+ width: 100%;
34
+ height: 100%;
35
+ justify-content: center;
36
+ align-items: center;
37
+ padding-left: 16px;
38
+ padding-right: 16px;
39
+ `;
40
+
41
+ export const HeadingContainer = styled.View`
42
+ padding: 20px 16px;
43
+ margin-top: -60px;
44
+ `;
45
+
46
+ export const CardsContainer = styled.View``;
47
+
48
+ const { width } = Dimensions.get('window');
49
+ const CARD_WIDTH = width - 60;
50
+ const CARD_WIDTH_SPACING = CARD_WIDTH + theme.spacing.l;
51
+ export const CardsList = styled.FlatList.attrs({
52
+ horizontal: true,
53
+ decelerationRate: 'fast',
54
+ showsHorizontalScrollIndicator: false,
55
+ snapToInterval: CARD_WIDTH_SPACING,
56
+ })`
57
+ margin-top: 20px;
58
+ height: 250px;
59
+ `;
60
+
61
+ export const HighlightsContainer = styled.View`
62
+ margin-top: ${RFValue(15)}px;
63
+ flex-direction: row;
64
+ justify-content: space-between;
65
+ flex-wrap: wrap;
66
+ `;
67
+
68
+ export const TitleSection = styled.View`
69
+ margin-top: 10px;
70
+ width: 100%;
71
+ justify-content: center;
72
+ padding: 10px 0;
73
+ `;
74
+
75
+ export const BannerImageContainer = styled.TouchableOpacity`
76
+ height: ${RFValue(200)}px;
77
+ border-radius: ${RFValue(10)}px;
78
+ background-color: ${({ theme }: any) => theme.colors.secondary};
79
+ `;
80
+
81
+ export const BannerImage = styled.Image`
82
+ height: 100%;
83
+ width: 100%;
84
+ border-radius: ${RFValue(5)}px;
85
+ `;
@@ -0,0 +1 @@
1
+ export { default as Home } from './Home';
@@ -0,0 +1,33 @@
1
+ import { apiSlice } from '@features/api/apiSlice';
2
+ import exampleApiSlice from '@features/example/exampleSlice';
3
+ import {
4
+ Action,
5
+ combineReducers,
6
+ configureStore,
7
+ ThunkAction,
8
+ } from '@reduxjs/toolkit';
9
+
10
+ const rootReducer = combineReducers({
11
+ [apiSlice.reducerPath]: apiSlice.reducer,
12
+ // Remova os slices de exemplo abaixo e adicione os seus próprios slices
13
+ example: exampleApiSlice.reducer,
14
+ });
15
+
16
+ export const setupStore = (preloadedState?: any) => {
17
+ return configureStore({
18
+ reducer: rootReducer,
19
+ preloadedState,
20
+ middleware: getDefaultMiddleware =>
21
+ getDefaultMiddleware().concat(apiSlice.middleware),
22
+ });
23
+ };
24
+
25
+ export type AppStore = ReturnType<typeof setupStore>;
26
+ export type AppDispatch = AppStore['dispatch'];
27
+ export type RootState = ReturnType<typeof rootReducer>;
28
+ export type AppThunk<ReturnType = void> = ThunkAction<
29
+ ReturnType,
30
+ RootState,
31
+ unknown,
32
+ Action<string>
33
+ >;
@@ -0,0 +1,8 @@
1
+ import 'styled-components';
2
+ import theme from '@theme/theme';
3
+
4
+ type Theme = typeof theme;
5
+
6
+ declare module 'styled-components' {
7
+ export interface DefaultTheme extends Theme {}
8
+ }
@@ -0,0 +1,93 @@
1
+ // Arquivo de Design Tokens
2
+ import styled from 'styled-components/native';
3
+
4
+ export default {
5
+ colors: {
6
+ background: {
7
+ primary: '#1E1E1E',
8
+ },
9
+ primary: '#512450',
10
+ secondary: '#f5aa3b',
11
+ success: {
12
+ primary: '#4cc38a',
13
+ light: '#81c784',
14
+ dark: '#388e3c',
15
+ },
16
+ info: {
17
+ primary: '#4fc3f7',
18
+ light: '',
19
+ dark: '',
20
+ },
21
+ warning: '#ffa726',
22
+ gray: '#8b8989',
23
+ grayDark: '#121212',
24
+ lightGray: '#b2b2b2',
25
+ light: '#fbfbfb',
26
+ white: '#fff',
27
+ black: '#000',
28
+ red: {
29
+ '50': '#F55C6C',
30
+ '100': '#c53030',
31
+ },
32
+ error: '#c53030',
33
+ icon: '#F55C6C',
34
+ },
35
+ fonts: {
36
+ light: 'Roboto_300Light',
37
+ regular: 'Roboto_400Regular',
38
+ medium: 'Roboto_500Medium',
39
+ bold: 'Roboto_700Bold',
40
+ extraBold: 'Roboto_900Black',
41
+ },
42
+ spacing: {
43
+ s: 8,
44
+ m: 18,
45
+ l: 20,
46
+ xl: 40,
47
+ },
48
+ sizes: {
49
+ title: 32,
50
+ h2: 24,
51
+ h3: 18,
52
+ body: 16,
53
+ radius: 5,
54
+ },
55
+ shadow: {
56
+ light: {
57
+ shadowColor: '#000',
58
+ shadowRadius: 4,
59
+ shadowOpacity: 0.1,
60
+ shadowOffset: {
61
+ width: 0,
62
+ height: 2,
63
+ },
64
+ },
65
+ dark: {
66
+ shadowColor: '#000',
67
+ shadowRadius: 4,
68
+ shadowOpacity: 0.3,
69
+ shadowOffset: {
70
+ width: 0,
71
+ height: 2,
72
+ },
73
+ },
74
+ },
75
+ };
76
+
77
+ export const Row = styled.View<{
78
+ justifyContent?: string;
79
+ alignItems?: string;
80
+ }>`
81
+ flex-direction: row;
82
+ justify-content: ${({ justifyContent }: any) => justifyContent || 'initial'};
83
+ align-items: ${({ alignItems }: any) => alignItems || 'initial'};
84
+ `;
85
+
86
+ export const Column = styled.View<{
87
+ justifyContent?: string;
88
+ alignItems?: string;
89
+ }>`
90
+ flex-direction: column;
91
+ justify-content: ${({ justifyContent }: any) => justifyContent || 'initial'};
92
+ align-items: ${({ alignItems }: any) => alignItems || 'initial'};
93
+ `;
@@ -0,0 +1 @@
1
+ export interface SignUpFormData {}
File without changes
package/tsconfig.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "extends": "expo/tsconfig.base",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "baseUrl": "./",
6
+ "jsx": "react-jsx",
7
+ "paths": {
8
+ "@src/*": [
9
+ "./src/*"
10
+ ],
11
+ "@assets/*": [
12
+ "./src/assets/*"
13
+ ],
14
+ "@components/*": [
15
+ "./src/components/*"
16
+ ],
17
+ "@features/*": [
18
+ "./src/features/*"
19
+ ],
20
+ "@routes/*": [
21
+ "./src/routes/*"
22
+ ],
23
+ "@screens/*": [
24
+ "./src/screens/*"
25
+ ],
26
+ "@hooks/*": [
27
+ "./src/hooks/*"
28
+ ],
29
+ "@theme/*": [
30
+ "./src/styles/*"
31
+ ],
32
+ "@contexts/*": [
33
+ "./src/contexts/*"
34
+ ],
35
+ "@utils/*": [
36
+ "./src/utils/*"
37
+ ]
38
+ }
39
+ }
40
+ }