@controleonline/ui-default 1.0.263
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/.github/agents/developer.agent.md +30 -0
- package/.github/agents/devops.agent.md +30 -0
- package/.github/agents/qa.agent.md +30 -0
- package/.github/agents/security.agent.md +30 -0
- package/.scrutinizer.yml +20 -0
- package/AGENTS.md +47 -0
- package/FUNDING.yml +1 -0
- package/package.json +21 -0
- package/src/react/components/errors/DefaultErrors.js +360 -0
- package/src/react/components/files/DefaultFile.js +46 -0
- package/src/react/components/filters/CompactFilterSelector.js +262 -0
- package/src/react/components/filters/CompactFilterSelector.styles.js +124 -0
- package/src/react/components/filters/DateShortcutFilter.js +264 -0
- package/src/react/components/filters/DateShortcutFilter.styles.js +82 -0
- package/src/react/components/filters/DefaultColumnFilter.js +97 -0
- package/src/react/components/filters/DefaultColumnFilter.styles.js +21 -0
- package/src/react/components/filters/DefaultExternalFilters.js +441 -0
- package/src/react/components/filters/DefaultExternalFilters.styles.js +177 -0
- package/src/react/components/filters/DefaultSearch.js +103 -0
- package/src/react/components/filters/DefaultSearch.styles.js +70 -0
- package/src/react/components/filters/dateFilterSelection.js +29 -0
- package/src/react/components/form/DefaultForm.js +198 -0
- package/src/react/components/form/DefaultForm.styles.js +70 -0
- package/src/react/components/help/DefaultTooltip.js +87 -0
- package/src/react/components/help/DefaultTooltip.styles.js +61 -0
- package/src/react/components/inputs/DefaultInput.js +160 -0
- package/src/react/components/inputs/DefaultInput.styles.js +93 -0
- package/src/react/components/inputs/DefaultSelect.js +192 -0
- package/src/react/components/inputs/DefaultSelect.styles.js +65 -0
- package/src/react/components/inputs/defaultInputUtils.js +230 -0
- package/src/react/components/map/DefaultGoogleMap.styles.js +9 -0
- package/src/react/components/map/DefaultGoogleMap.web.js +698 -0
- package/src/react/components/map/DefaultMap.native.js +71 -0
- package/src/react/components/map/DefaultMap.shared.js +762 -0
- package/src/react/components/map/DefaultMap.styles.js +16 -0
- package/src/react/components/map/DefaultMap.web.js +66 -0
- package/src/react/components/map/DefaultNativeMap.native.js +615 -0
- package/src/react/components/map/DefaultNativeMap.shared.js +532 -0
- package/src/react/components/map/DefaultNativeMap.styles.js +122 -0
- package/src/react/components/table/DefaultTable.js +1897 -0
- package/src/react/components/table/DefaultTable.styles.js +610 -0
- package/src/react/index.js +10 -0
- package/src/react/utils/tableVisibleColumnsPreferences.js +264 -0
- package/src/store/default/actions.js +444 -0
- package/src/store/default/getters.js +28 -0
- package/src/store/default/mutation_types.js +26 -0
- package/src/store/default/mutations.js +138 -0
- package/src/tests/react/components/DateShortcutFilter.test.js +96 -0
- package/src/tests/react/components/errors/DefaultErrors.test.js +162 -0
- package/src/tests/react/components/files/DefaultFile.test.js +80 -0
- package/src/tests/react/components/map/DefaultMap.shared.test.js +162 -0
- package/src/tests/react/filters/dateFilterSelection.test.js +46 -0
- package/src/tests/react/inputs/defaultInputUtils.test.js +45 -0
- package/src/tests/react/store/defaultActions.test.js +112 -0
- package/src/tests/react/utils/tableVisibleColumnsPreferences.test.js +223 -0
- package/src/utils/filters.js +56 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Developer
|
|
3
|
+
description: Executor autônomo de issues do repositório ControleOnline/ui-default, com fonte canônica centralizada no cto-mcp.
|
|
4
|
+
target: github-copilot
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fonte canônica
|
|
8
|
+
|
|
9
|
+
Este wrapper é intencionalmente fino. Antes de agir, leia e siga, nesta ordem:
|
|
10
|
+
|
|
11
|
+
1. `https://github.com/ControleOnline/cto-mcp/blob/master/agents/agent/developer/agent.md`
|
|
12
|
+
|
|
13
|
+
Esse arquivo central referencia as regras-base de `automation/` no `cto-mcp`. Se este wrapper local divergir do conteúdo canônico do `cto-mcp`, prefira o `cto-mcp`, salvo quando o estado real deste repositório exigir adaptação operacional explícita.
|
|
14
|
+
|
|
15
|
+
## Contexto local
|
|
16
|
+
|
|
17
|
+
Você está operando no repositório `ControleOnline/ui-default`.
|
|
18
|
+
|
|
19
|
+
Você conhece o ecossistema completo da ControleOnline. Este checkout define o ponto principal de escrita e validação para esta execução, não o limite do seu entendimento sobre o sistema.
|
|
20
|
+
|
|
21
|
+
- Checkout local: `app-community/modules/controleonline/ui-default`
|
|
22
|
+
- Tipo: submódulo de app-community
|
|
23
|
+
- Família: frontend
|
|
24
|
+
- Branch base operacional: `master`
|
|
25
|
+
- Alvo preferencial de PR: `dev`
|
|
26
|
+
- `AGENTS.md` local: presente
|
|
27
|
+
|
|
28
|
+
Leia o `AGENTS.md` mais próximo antes de editar código. Se esta alteração tocar apenas o repositório atual, trabalhe aqui; se também exigir atualização do superprojeto que consome este repositório, registre ou entregue a composição necessária sem perder a separação de ownership.
|
|
29
|
+
|
|
30
|
+
_Arquivo gerado por `cto-mcp/scripts/sync-copilot-agents.mjs`._
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: DevOps
|
|
3
|
+
description: Operador de fluxo e automações do repositório ControleOnline/ui-default, com fonte canônica centralizada no cto-mcp.
|
|
4
|
+
target: github-copilot
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fonte canônica
|
|
8
|
+
|
|
9
|
+
Este wrapper é intencionalmente fino. Antes de agir, leia e siga, nesta ordem:
|
|
10
|
+
|
|
11
|
+
1. `https://github.com/ControleOnline/cto-mcp/blob/master/agents/agent/devops/agent.md`
|
|
12
|
+
|
|
13
|
+
Esse arquivo central referencia as regras-base de `automation/` no `cto-mcp`. Se este wrapper local divergir do conteúdo canônico do `cto-mcp`, prefira o `cto-mcp`, salvo quando o estado real deste repositório exigir adaptação operacional explícita.
|
|
14
|
+
|
|
15
|
+
## Contexto local
|
|
16
|
+
|
|
17
|
+
Você está operando no repositório `ControleOnline/ui-default`.
|
|
18
|
+
|
|
19
|
+
Você conhece o ecossistema completo da ControleOnline. Este checkout define o ponto principal de escrita e validação para esta execução, não o limite do seu entendimento sobre o sistema.
|
|
20
|
+
|
|
21
|
+
- Checkout local: `app-community/modules/controleonline/ui-default`
|
|
22
|
+
- Tipo: submódulo de app-community
|
|
23
|
+
- Família: frontend
|
|
24
|
+
- Branch base operacional: `master`
|
|
25
|
+
- Alvo preferencial de PR: `dev`
|
|
26
|
+
- `AGENTS.md` local: presente
|
|
27
|
+
|
|
28
|
+
Leia o `AGENTS.md` mais próximo antes de editar código. Se esta alteração tocar apenas o repositório atual, trabalhe aqui; se também exigir atualização do superprojeto que consome este repositório, registre ou entregue a composição necessária sem perder a separação de ownership.
|
|
29
|
+
|
|
30
|
+
_Arquivo gerado por `cto-mcp/scripts/sync-copilot-agents.mjs`._
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Quality Assurance
|
|
3
|
+
description: Revisor técnico de entregas do repositório ControleOnline/ui-default, com fonte canônica centralizada no cto-mcp.
|
|
4
|
+
target: github-copilot
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fonte canônica
|
|
8
|
+
|
|
9
|
+
Este wrapper é intencionalmente fino. Antes de agir, leia e siga, nesta ordem:
|
|
10
|
+
|
|
11
|
+
1. `https://github.com/ControleOnline/cto-mcp/blob/master/agents/agent/qa/agent.md`
|
|
12
|
+
|
|
13
|
+
Esse arquivo central referencia as regras-base de `automation/` no `cto-mcp`. Se este wrapper local divergir do conteúdo canônico do `cto-mcp`, prefira o `cto-mcp`, salvo quando o estado real deste repositório exigir adaptação operacional explícita.
|
|
14
|
+
|
|
15
|
+
## Contexto local
|
|
16
|
+
|
|
17
|
+
Você está operando no repositório `ControleOnline/ui-default`.
|
|
18
|
+
|
|
19
|
+
Você conhece o ecossistema completo da ControleOnline. Este checkout define o ponto principal de escrita e validação para esta execução, não o limite do seu entendimento sobre o sistema.
|
|
20
|
+
|
|
21
|
+
- Checkout local: `app-community/modules/controleonline/ui-default`
|
|
22
|
+
- Tipo: submódulo de app-community
|
|
23
|
+
- Família: frontend
|
|
24
|
+
- Branch base operacional: `master`
|
|
25
|
+
- Alvo preferencial de PR: `dev`
|
|
26
|
+
- `AGENTS.md` local: presente
|
|
27
|
+
|
|
28
|
+
Leia o `AGENTS.md` mais próximo antes de editar código. Se esta alteração tocar apenas o repositório atual, trabalhe aqui; se também exigir atualização do superprojeto que consome este repositório, registre ou entregue a composição necessária sem perder a separação de ownership.
|
|
29
|
+
|
|
30
|
+
_Arquivo gerado por `cto-mcp/scripts/sync-copilot-agents.mjs`._
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Security
|
|
3
|
+
description: Revisor de segurança do repositório ControleOnline/ui-default, com fonte canônica centralizada no cto-mcp.
|
|
4
|
+
target: github-copilot
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Fonte canônica
|
|
8
|
+
|
|
9
|
+
Este wrapper é intencionalmente fino. Antes de agir, leia e siga, nesta ordem:
|
|
10
|
+
|
|
11
|
+
1. `https://github.com/ControleOnline/cto-mcp/blob/master/agents/agent/security/agent.md`
|
|
12
|
+
|
|
13
|
+
Esse arquivo central referencia as regras-base de `automation/` no `cto-mcp`. Se este wrapper local divergir do conteúdo canônico do `cto-mcp`, prefira o `cto-mcp`, salvo quando o estado real deste repositório exigir adaptação operacional explícita.
|
|
14
|
+
|
|
15
|
+
## Contexto local
|
|
16
|
+
|
|
17
|
+
Você está operando no repositório `ControleOnline/ui-default`.
|
|
18
|
+
|
|
19
|
+
Você conhece o ecossistema completo da ControleOnline. Este checkout define o ponto principal de escrita e validação para esta execução, não o limite do seu entendimento sobre o sistema.
|
|
20
|
+
|
|
21
|
+
- Checkout local: `app-community/modules/controleonline/ui-default`
|
|
22
|
+
- Tipo: submódulo de app-community
|
|
23
|
+
- Família: frontend
|
|
24
|
+
- Branch base operacional: `master`
|
|
25
|
+
- Alvo preferencial de PR: `dev`
|
|
26
|
+
- `AGENTS.md` local: presente
|
|
27
|
+
|
|
28
|
+
Leia o `AGENTS.md` mais próximo antes de editar código. Se esta alteração tocar apenas o repositório atual, trabalhe aqui; se também exigir atualização do superprojeto que consome este repositório, registre ou entregue a composição necessária sem perder a separação de ownership.
|
|
29
|
+
|
|
30
|
+
_Arquivo gerado por `cto-mcp/scripts/sync-copilot-agents.mjs`._
|
package/.scrutinizer.yml
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
checks:
|
|
2
|
+
javascript: true
|
|
3
|
+
|
|
4
|
+
build:
|
|
5
|
+
nodes:
|
|
6
|
+
analysis:
|
|
7
|
+
environment:
|
|
8
|
+
node: v16
|
|
9
|
+
project_setup:
|
|
10
|
+
override: true
|
|
11
|
+
tests:
|
|
12
|
+
override:
|
|
13
|
+
- js-scrutinizer-run
|
|
14
|
+
|
|
15
|
+
filter:
|
|
16
|
+
excluded_paths:
|
|
17
|
+
- "build/*"
|
|
18
|
+
- "coverage/*"
|
|
19
|
+
- "dist/*"
|
|
20
|
+
- "node_modules/*"
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# ui-default
|
|
2
|
+
|
|
3
|
+
## Escopo
|
|
4
|
+
|
|
5
|
+
- Biblioteca base de componentes reutilizaveis do front.
|
|
6
|
+
- `DefaultTable` e o shell canonico de listagens React deste sistema.
|
|
7
|
+
|
|
8
|
+
## Contrato do DefaultTable
|
|
9
|
+
|
|
10
|
+
- A tela deve passar preferencialmente apenas `storeName` e contexto minimo, como `requestParams`, `onRowPress`, `renderCard`, `onAdd`, `onEdit` ou componentes extras realmente especificos.
|
|
11
|
+
- O componente pode receber componentes de composicao para variacoes visuais, como `renderCard`, add personalizado, toolbar customizada, acoes auxiliares e modais de apoio; isso nao muda o contrato de que a listagem continua sendo responsabilidade do `DefaultTable`.
|
|
12
|
+
- Quando `data` nao e informada e existe `storeName`, o `DefaultTable` trabalha em modo automatico e carrega os itens pelas actions do store.
|
|
13
|
+
- O componente e responsavel por busca, ordenacao, filtros, paginação, infinite scroll, loading, erro, resumo, visibilidade de colunas, modo desktop e modo card no mobile.
|
|
14
|
+
- O store nao e fallback: ele e a regra. Se `columns` define um comportamento, o `DefaultTable` deve obedecer sem recriar uma segunda regra na tela.
|
|
15
|
+
- `columns` e a fonte de verdade para label, visibilidade, ordem, `editable`, `sortable`, `inputType`, `list`, `format`, `formatList`, `formatFilter`, `saveFormat`, `searchParam`, `externalFilter`, `summary` e `defaultSort`. `show:false` e um alias aceito para ocultar coluna, junto com `visible:false` e `table:false`.
|
|
16
|
+
- Se uma coluna vier como `editable: false`, ela continua nao editavel mesmo que seja ordenavel. Se vier como `sortable: true`, a ordenacao precisa continuar funcionando.
|
|
17
|
+
- `inputType: 'date-range'` exige filtro de intervalo. `externalFilter: true` habilita o filtro externo quando a listagem suporta esse contrato.
|
|
18
|
+
- Nada de search local, sort local, paginacao manual, contador local, total calculado por `reduce` ou cards paralelos quando o `DefaultTable` ja puder resolver isso pelo store.
|
|
19
|
+
- `requestParams` serve para contexto fixo do fluxo, nao para duplicar estado de filtro que ja vive no store.
|
|
20
|
+
- `pageSize` so deve ser usado quando o contrato externo exigir e com justificativa clara no modulo.
|
|
21
|
+
- `summary` e `totalItems` vem do backend/store e pertencem ao rodape interno do `DefaultTable`.
|
|
22
|
+
- `add: true` abre o `DefaultForm` quando a tela nao prover `onAdd`.
|
|
23
|
+
- O modo controlado continua existindo apenas para excecoes reais de integracao; o padrao do sistema e o modo store-driven.
|
|
24
|
+
- Loadings e erros devem seguir o fluxo central do sistema, sem banners ou estados paralelos na tela.
|
|
25
|
+
- O `get` do store default pode preservar o `item` atual durante refresh quando receber `__storeMeta.preserveItem = true`; use esse modo quando a tela precisa atualizar o registro sem desmontar o detalhe em exibição.
|
|
26
|
+
- Explicacoes permanentes de tela devem sair do corpo principal e ir para o `DefaultTooltip`, acionado por `?`, quando houver contexto necessario para o usuario.
|
|
27
|
+
|
|
28
|
+
## Componentes de feedback
|
|
29
|
+
|
|
30
|
+
- `StateStore` e o shell compartilhado para loading e saving de tela/seção.
|
|
31
|
+
- O `mode` do `StateStore` e um preset generico do shell e pode variar conforme a tela; exemplos validos incluem `compact`, `display` e outros modos reais do fluxo.
|
|
32
|
+
- `DefaultErrors` e o shell compartilhado para erro local/inline de tela ou tab; ele lê o `error` dos stores informados, aparece como popup, auto-fecha em 5 segundos e limpa o erro do store ao ser fechado.
|
|
33
|
+
- `showError` continua sendo feedback transitório global; nao deve substituir `DefaultErrors` quando o erro for inline ou local.
|
|
34
|
+
- Telas e componentes visuais nao devem usar `ActivityIndicator` direto quando o estado puder ser representado por `StateStore`.
|
|
35
|
+
- O contrato de explicacao permanente continua sendo `DefaultTooltip`, sempre acionado por `?` e fora do corpo principal da tela.
|
|
36
|
+
|
|
37
|
+
## Arquivos e imagens
|
|
38
|
+
|
|
39
|
+
- `DefaultFile` e o componente compartilhado para arquivos/imagens vindos do backend.
|
|
40
|
+
- Quando a fonte for um arquivo de download do backend, o componente deve resolver a URL com `app-domain=<dominio configurado>` e enviar o mesmo valor em `headers` quando a plataforma permitir.
|
|
41
|
+
- Telas que exibem logo, fundo, avatar, banner ou qualquer arquivo de empresa devem preferir `DefaultFile` em vez de `Image` direto quando o contrato vier do backend.
|
|
42
|
+
|
|
43
|
+
## Regra de uso
|
|
44
|
+
|
|
45
|
+
- Telas antigas devem ser simplificadas para passar o store e apenas as excecoes declarativas que o store nao cobre.
|
|
46
|
+
- Se a listagem precisa de outra regra de exibicao, a primeira opcao e mover essa regra para `columns` ou para o store correspondente.
|
|
47
|
+
- Se duas telas repetem a mesma logica de listagem, a resposta e um componente default ou um ajuste no `DefaultTable`, nunca uma copia local do comportamento.
|
package/FUNDING.yml
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
github: [controleonline]
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@controleonline/ui-default",
|
|
3
|
+
"version": "1.0.263",
|
|
4
|
+
"author": "ControleOnline",
|
|
5
|
+
"description": "ControleOnline Default Quasar UI Component",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"main": "src/react/index.js",
|
|
8
|
+
"scripts": {},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/ControleOnline/conline-quasar-ui"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "",
|
|
14
|
+
"homepage": "https://www.controleonline.com/devs/plugins",
|
|
15
|
+
"browserslist": [
|
|
16
|
+
"last 1 version, not dead, ie >= 11"
|
|
17
|
+
],
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@controleonline/ui-legacy": "*"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
|
|
2
|
+
import {Modal, Pressable, Text, TouchableOpacity, View} from 'react-native';
|
|
3
|
+
import {useStore, useStores} from '@store';
|
|
4
|
+
import resolveSystemErrorMessage from '@controleonline/ui-common/src/react/utils/systemErrorMessage';
|
|
5
|
+
|
|
6
|
+
const AUTO_CLOSE_MS = 5000;
|
|
7
|
+
|
|
8
|
+
const resolveText = value => String(value ?? '').trim();
|
|
9
|
+
|
|
10
|
+
const normalizeStoreNames = (store, stores) => {
|
|
11
|
+
const resolvedStoreNames = [
|
|
12
|
+
...(Array.isArray(store) ? store : store ? [store] : []),
|
|
13
|
+
...(Array.isArray(stores) ? stores : []),
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
return resolvedStoreNames.filter(name => typeof name === 'string' && name.trim()).map(name => name.trim());
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const pickThemeColor = (colors, keys, fallback) => {
|
|
20
|
+
for (const key of keys) {
|
|
21
|
+
const value = colors?.[key];
|
|
22
|
+
|
|
23
|
+
if (typeof value === 'string' && value.trim()) {
|
|
24
|
+
return value.trim();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fallback;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const DefaultErrors = ({
|
|
32
|
+
store = null,
|
|
33
|
+
stores = [],
|
|
34
|
+
error = null,
|
|
35
|
+
title = '',
|
|
36
|
+
message = '',
|
|
37
|
+
compact = false,
|
|
38
|
+
align = 'center',
|
|
39
|
+
onRetry = null,
|
|
40
|
+
retryLabel = 'Tentar novamente',
|
|
41
|
+
containerStyle = null,
|
|
42
|
+
contentStyle = null,
|
|
43
|
+
titleStyle = null,
|
|
44
|
+
messageStyle = null,
|
|
45
|
+
retryButtonStyle = null,
|
|
46
|
+
retryTextStyle = null,
|
|
47
|
+
}) => {
|
|
48
|
+
const themeStore = useStore('theme');
|
|
49
|
+
const themeColors = themeStore?.getters?.colors || {};
|
|
50
|
+
const allStores = typeof useStores === 'function' ? useStores(state => state) : null;
|
|
51
|
+
const storeNames = useMemo(() => normalizeStoreNames(store, stores), [store, stores]);
|
|
52
|
+
const watchedStoreEntries = useMemo(() => {
|
|
53
|
+
if (!allStores || storeNames.length === 0) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return storeNames
|
|
58
|
+
.map(storeName => {
|
|
59
|
+
const currentStore = allStores?.[storeName] || null;
|
|
60
|
+
const storeError = resolveSystemErrorMessage(currentStore?.getters?.error);
|
|
61
|
+
|
|
62
|
+
if (!storeError) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
error: storeError,
|
|
68
|
+
name: storeName,
|
|
69
|
+
store: currentStore,
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
}, [allStores, storeNames]);
|
|
74
|
+
|
|
75
|
+
const explicitTitle = resolveText(title);
|
|
76
|
+
const explicitMessage = resolveText(message) || resolveSystemErrorMessage(error);
|
|
77
|
+
const hasStoreError = watchedStoreEntries.length > 0;
|
|
78
|
+
const activeStoreEntry = watchedStoreEntries[0] || null;
|
|
79
|
+
const visibleMessage = hasStoreError ? activeStoreEntry?.error || '' : explicitMessage;
|
|
80
|
+
const hasExplicitContent = storeNames.length === 0
|
|
81
|
+
? Boolean(explicitTitle || explicitMessage || typeof onRetry === 'function')
|
|
82
|
+
: Boolean(explicitMessage || typeof onRetry === 'function');
|
|
83
|
+
const storeDriverSignature = hasStoreError
|
|
84
|
+
? `store:${watchedStoreEntries.map(entry => `${entry.name}:${entry.error}`).join('|')}`
|
|
85
|
+
: '';
|
|
86
|
+
const explicitDriverSignature = hasExplicitContent
|
|
87
|
+
? `explicit:${[explicitTitle, explicitMessage, typeof onRetry === 'function' ? 'retry' : '']
|
|
88
|
+
.map(value => resolveText(value))
|
|
89
|
+
.filter(Boolean)
|
|
90
|
+
.join('|')}`
|
|
91
|
+
: '';
|
|
92
|
+
const activeDriverSignature = storeDriverSignature || explicitDriverSignature;
|
|
93
|
+
const [dismissedSignatures, setDismissedSignatures] = useState(() => new Set());
|
|
94
|
+
const timerRef = useRef(null);
|
|
95
|
+
const textAlign = align === 'left' ? 'left' : 'center';
|
|
96
|
+
const accentColor = pickThemeColor(
|
|
97
|
+
themeColors,
|
|
98
|
+
['inputErrorBorder', 'textDanger', 'modalBorder', 'primary'],
|
|
99
|
+
'#DC2626',
|
|
100
|
+
);
|
|
101
|
+
const backgroundColor = pickThemeColor(
|
|
102
|
+
themeColors,
|
|
103
|
+
['inputErrorBackground', 'modalBackground', 'surface', 'background'],
|
|
104
|
+
'#FFFFFF',
|
|
105
|
+
);
|
|
106
|
+
const overlayColor = pickThemeColor(
|
|
107
|
+
themeColors,
|
|
108
|
+
['modalOverlay', 'overlayBackground'],
|
|
109
|
+
'rgba(15, 23, 42, 0.55)',
|
|
110
|
+
);
|
|
111
|
+
const titleColor = pickThemeColor(
|
|
112
|
+
themeColors,
|
|
113
|
+
['inputErrorText', 'textDanger', 'modalHeaderText', 'text'],
|
|
114
|
+
'#B91C1C',
|
|
115
|
+
);
|
|
116
|
+
const messageColor = pickThemeColor(
|
|
117
|
+
themeColors,
|
|
118
|
+
['modalText', 'textSecondary', 'text'],
|
|
119
|
+
'#334155',
|
|
120
|
+
);
|
|
121
|
+
const closeColor = pickThemeColor(
|
|
122
|
+
themeColors,
|
|
123
|
+
['modalCloseIcon', 'textDanger', 'primary'],
|
|
124
|
+
accentColor,
|
|
125
|
+
);
|
|
126
|
+
const retryAccentColor = pickThemeColor(themeColors, ['primary'], accentColor);
|
|
127
|
+
|
|
128
|
+
const clearStoreErrors = useCallback(() => {
|
|
129
|
+
watchedStoreEntries.forEach(entry => {
|
|
130
|
+
const setError = entry?.store?.actions?.setError;
|
|
131
|
+
|
|
132
|
+
if (typeof setError === 'function') {
|
|
133
|
+
setError('');
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
}, [watchedStoreEntries]);
|
|
137
|
+
|
|
138
|
+
const dismissPopup = useCallback(() => {
|
|
139
|
+
if (timerRef.current) {
|
|
140
|
+
clearTimeout(timerRef.current);
|
|
141
|
+
timerRef.current = null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
setDismissedSignatures(previous => {
|
|
145
|
+
const next = new Set(previous);
|
|
146
|
+
|
|
147
|
+
if (storeDriverSignature) {
|
|
148
|
+
next.add(storeDriverSignature);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (explicitDriverSignature) {
|
|
152
|
+
next.add(explicitDriverSignature);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return next;
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
clearStoreErrors();
|
|
159
|
+
}, [clearStoreErrors, explicitDriverSignature, storeDriverSignature]);
|
|
160
|
+
|
|
161
|
+
const isVisible = Boolean(activeDriverSignature) && !dismissedSignatures.has(activeDriverSignature);
|
|
162
|
+
const hasContent = Boolean(explicitTitle || visibleMessage || typeof onRetry === 'function');
|
|
163
|
+
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!activeDriverSignature) {
|
|
166
|
+
if (timerRef.current) {
|
|
167
|
+
clearTimeout(timerRef.current);
|
|
168
|
+
timerRef.current = null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
setDismissedSignatures(previous => (previous.size > 0 ? new Set() : previous));
|
|
172
|
+
return undefined;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (!isVisible) {
|
|
176
|
+
if (timerRef.current) {
|
|
177
|
+
clearTimeout(timerRef.current);
|
|
178
|
+
timerRef.current = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (timerRef.current) {
|
|
185
|
+
clearTimeout(timerRef.current);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
timerRef.current = setTimeout(() => {
|
|
189
|
+
dismissPopup();
|
|
190
|
+
}, AUTO_CLOSE_MS);
|
|
191
|
+
|
|
192
|
+
return () => {
|
|
193
|
+
if (timerRef.current) {
|
|
194
|
+
clearTimeout(timerRef.current);
|
|
195
|
+
timerRef.current = null;
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}, [activeDriverSignature, dismissPopup, isVisible]);
|
|
199
|
+
|
|
200
|
+
if (!isVisible || !hasContent) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return (
|
|
205
|
+
<Modal
|
|
206
|
+
animationType="fade"
|
|
207
|
+
onRequestClose={dismissPopup}
|
|
208
|
+
transparent
|
|
209
|
+
visible={isVisible}
|
|
210
|
+
>
|
|
211
|
+
<Pressable
|
|
212
|
+
accessibilityRole="button"
|
|
213
|
+
onPress={dismissPopup}
|
|
214
|
+
style={[
|
|
215
|
+
{
|
|
216
|
+
alignItems: 'center',
|
|
217
|
+
backgroundColor: overlayColor,
|
|
218
|
+
flex: 1,
|
|
219
|
+
justifyContent: 'center',
|
|
220
|
+
padding: 18,
|
|
221
|
+
},
|
|
222
|
+
]}
|
|
223
|
+
>
|
|
224
|
+
<View
|
|
225
|
+
onStartShouldSetResponder={() => true}
|
|
226
|
+
style={[
|
|
227
|
+
{
|
|
228
|
+
backgroundColor,
|
|
229
|
+
borderColor: accentColor,
|
|
230
|
+
borderRadius: 16,
|
|
231
|
+
borderWidth: 1,
|
|
232
|
+
maxWidth: compact ? 360 : 440,
|
|
233
|
+
padding: compact ? 14 : 16,
|
|
234
|
+
shadowColor: accentColor,
|
|
235
|
+
shadowOffset: {height: 10, width: 0},
|
|
236
|
+
shadowOpacity: 0.12,
|
|
237
|
+
shadowRadius: 18,
|
|
238
|
+
width: '100%',
|
|
239
|
+
elevation: 12,
|
|
240
|
+
},
|
|
241
|
+
containerStyle,
|
|
242
|
+
]}
|
|
243
|
+
>
|
|
244
|
+
<View
|
|
245
|
+
style={[
|
|
246
|
+
{
|
|
247
|
+
alignItems: 'flex-start',
|
|
248
|
+
flexDirection: 'row',
|
|
249
|
+
gap: 10,
|
|
250
|
+
justifyContent: 'space-between',
|
|
251
|
+
},
|
|
252
|
+
contentStyle,
|
|
253
|
+
]}
|
|
254
|
+
>
|
|
255
|
+
<View style={{flex: 1, gap: explicitTitle ? 10 : 8}}>
|
|
256
|
+
{explicitTitle ? (
|
|
257
|
+
<Text
|
|
258
|
+
style={[
|
|
259
|
+
{
|
|
260
|
+
color: titleColor,
|
|
261
|
+
fontSize: compact ? 14 : 15,
|
|
262
|
+
fontWeight: '800',
|
|
263
|
+
textAlign,
|
|
264
|
+
},
|
|
265
|
+
titleStyle,
|
|
266
|
+
]}
|
|
267
|
+
>
|
|
268
|
+
{explicitTitle}
|
|
269
|
+
</Text>
|
|
270
|
+
) : null}
|
|
271
|
+
|
|
272
|
+
{visibleMessage ? (
|
|
273
|
+
<Text
|
|
274
|
+
style={[
|
|
275
|
+
{
|
|
276
|
+
color: messageColor,
|
|
277
|
+
fontSize: compact ? 12 : 13,
|
|
278
|
+
lineHeight: compact ? 18 : 19,
|
|
279
|
+
textAlign,
|
|
280
|
+
},
|
|
281
|
+
messageStyle,
|
|
282
|
+
]}
|
|
283
|
+
>
|
|
284
|
+
{visibleMessage}
|
|
285
|
+
</Text>
|
|
286
|
+
) : null}
|
|
287
|
+
|
|
288
|
+
{typeof onRetry === 'function' ? (
|
|
289
|
+
<TouchableOpacity
|
|
290
|
+
accessibilityRole="button"
|
|
291
|
+
activeOpacity={0.85}
|
|
292
|
+
onPress={onRetry}
|
|
293
|
+
style={[
|
|
294
|
+
{
|
|
295
|
+
alignItems: 'center',
|
|
296
|
+
alignSelf: textAlign === 'left' ? 'flex-start' : 'center',
|
|
297
|
+
borderRadius: 999,
|
|
298
|
+
borderWidth: 1,
|
|
299
|
+
borderColor: retryAccentColor,
|
|
300
|
+
backgroundColor: `${retryAccentColor}14`,
|
|
301
|
+
marginTop: 4,
|
|
302
|
+
paddingHorizontal: 12,
|
|
303
|
+
paddingVertical: 8,
|
|
304
|
+
},
|
|
305
|
+
retryButtonStyle,
|
|
306
|
+
]}
|
|
307
|
+
>
|
|
308
|
+
<Text
|
|
309
|
+
style={[
|
|
310
|
+
{
|
|
311
|
+
color: retryAccentColor,
|
|
312
|
+
fontSize: 12,
|
|
313
|
+
fontWeight: '800',
|
|
314
|
+
},
|
|
315
|
+
retryTextStyle,
|
|
316
|
+
]}
|
|
317
|
+
>
|
|
318
|
+
{retryLabel}
|
|
319
|
+
</Text>
|
|
320
|
+
</TouchableOpacity>
|
|
321
|
+
) : null}
|
|
322
|
+
</View>
|
|
323
|
+
|
|
324
|
+
<TouchableOpacity
|
|
325
|
+
accessibilityLabel="Fechar erro"
|
|
326
|
+
accessibilityRole="button"
|
|
327
|
+
activeOpacity={0.8}
|
|
328
|
+
onPress={dismissPopup}
|
|
329
|
+
testID="default-errors-close"
|
|
330
|
+
style={[
|
|
331
|
+
{
|
|
332
|
+
alignItems: 'center',
|
|
333
|
+
borderColor: accentColor,
|
|
334
|
+
borderRadius: 999,
|
|
335
|
+
borderWidth: 1,
|
|
336
|
+
height: 28,
|
|
337
|
+
justifyContent: 'center',
|
|
338
|
+
width: 28,
|
|
339
|
+
},
|
|
340
|
+
]}
|
|
341
|
+
>
|
|
342
|
+
<Text
|
|
343
|
+
style={{
|
|
344
|
+
color: closeColor,
|
|
345
|
+
fontSize: 16,
|
|
346
|
+
fontWeight: '800',
|
|
347
|
+
lineHeight: 16,
|
|
348
|
+
}}
|
|
349
|
+
>
|
|
350
|
+
×
|
|
351
|
+
</Text>
|
|
352
|
+
</TouchableOpacity>
|
|
353
|
+
</View>
|
|
354
|
+
</View>
|
|
355
|
+
</Pressable>
|
|
356
|
+
</Modal>
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
export default DefaultErrors;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React, {memo, useMemo} from 'react';
|
|
2
|
+
import {Image} from 'react-native';
|
|
3
|
+
import {useStore} from '@store';
|
|
4
|
+
import {resolveDefaultFileSource} from '@controleonline/ui-common/src/react/utils/fileUrl';
|
|
5
|
+
|
|
6
|
+
const resolveCompanyFromStore = peopleGetters => {
|
|
7
|
+
if (peopleGetters?.defaultCompany?.id) {
|
|
8
|
+
return peopleGetters.defaultCompany;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (peopleGetters?.currentCompany?.id) {
|
|
12
|
+
return peopleGetters.currentCompany;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const DefaultFile = ({
|
|
19
|
+
file,
|
|
20
|
+
source,
|
|
21
|
+
company,
|
|
22
|
+
appDomain = '',
|
|
23
|
+
headers = {},
|
|
24
|
+
...imageProps
|
|
25
|
+
}) => {
|
|
26
|
+
const peopleStore = useStore('people');
|
|
27
|
+
const peopleGetters = peopleStore?.getters || {};
|
|
28
|
+
const resolvedCompany = company || resolveCompanyFromStore(peopleGetters);
|
|
29
|
+
const resolvedSource = useMemo(
|
|
30
|
+
() =>
|
|
31
|
+
resolveDefaultFileSource(source ?? file, {
|
|
32
|
+
company: resolvedCompany,
|
|
33
|
+
appDomain,
|
|
34
|
+
headers,
|
|
35
|
+
}),
|
|
36
|
+
[appDomain, company, file, headers, resolvedCompany, source],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (!resolvedSource) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return <Image {...imageProps} source={resolvedSource} />;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export default memo(DefaultFile);
|