Commit 06b6c363 authored by Rafael's avatar Rafael

Adiciona conexões com o serviço e primeiro acesso no simulador.

parent 533c4f22
--registry "https://registry.npmjs.org/" --registry "https://nexus.dev.evologica.com.br/repository/npm"
"@curio:registry" "https://nexus.dev.evologica.com.br/repository/npm" "@curio:registry" "https://nexus.dev.evologica.com.br/repository/npm"
"@dynamo:registry" "https://nexus.dev.evologica.com.br/repository/npm" "@dynamo:registry" "https://nexus.dev.evologica.com.br/repository/npm"
\ No newline at end of file
...@@ -11,14 +11,7 @@ ...@@ -11,14 +11,7 @@
"useBuiltIns": "usage", "useBuiltIns": "usage",
"targets": { "targets": {
"ie": "11", "ie": "11",
"browsers": [ "browsers": ["edge >= 16", "safari >= 9", "firefox >= 57", "ie >= 11", "ios >= 9", "chrome >= 49"]
"edge >= 16",
"safari >= 9",
"firefox >= 57",
"ie >= 11",
"ios >= 9",
"chrome >= 49"
]
} }
} }
], ],
...@@ -58,6 +51,7 @@ ...@@ -58,6 +51,7 @@
"@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-class-properties",
"@babel/plugin-transform-async-to-generator", "@babel/plugin-transform-async-to-generator",
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
"@babel/plugin-transform-arrow-functions" "@babel/plugin-transform-arrow-functions",
"@babel/plugin-syntax-top-level-await"
] ]
} }
{ {
"service": { "API_URL": "http://172.16.17.3:8080",
"url": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/gatewayJSON?version=4", "APP_NAME_BROKER": "@microcredito-dev/agente",
"server": "192.168.0.34", "APP_NAME_CUSTOMER": "@microcredito-dev/cliente",
"system": 19, "SESSION_KEY_BROKER": "@microcredito-dev/agente",
"port": 9801, "SESSION_KEY_CUSTOMER": "@microcredito-dev/cliente"
"module": 1,
"version": 3
},
"accessToken": "ab4dfc4ab84517f900193f8a2530680c73ad3539f75e1f5661bee7065cfcdd32",
"resources": {
"get": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/getpr",
"put": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/putpr"
},
"logs": true
} }
{
"API_URL": "http://192.168.0.34:8080",
"APP_NAME_BROKER": "@microcredito-dev/agente",
"APP_NAME_CUSTOMER": "@microcredito-dev/cliente",
"SESSION_KEY_BROKER": "@microcredito-dev/agente",
"SESSION_KEY_CUSTOMER": "@microcredito-dev/cliente"
}
\ No newline at end of file
{ {
"service": { "API_URL": "https://microcredito.dev.evologica.com.br",
"url": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/gatewayJSON?version=4", "APP_NAME_BROKER": "@microcredito/agente",
"server": "192.168.0.34", "APP_NAME_CUSTOMER": "@microcredito/cliente",
"system": 19, "SESSION_KEY_BROKER": "@microcredito/agente",
"port": 9801, "SESSION_KEY_CUSTOMER": "@microcredito/cliente"
"module": 1,
"version": 3
},
"accessToken": "ab4dfc4ab84517f900193f8a2530680c73ad3539f75e1f5661bee7065cfcdd32",
"resources": {
"get": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/getpr",
"put": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/putpr"
},
"logs": true
} }
{ {
"service": { "API_URL": "https://microcredito.test.evologica.com.br",
"url": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/gatewayJSON?version=4", "APP_NAME_BROKER": "@microcredito-staging/agente",
"server": "192.168.0.34", "APP_NAME_CUSTOMER": "@microcredito-staging/cliente",
"system": 19, "SESSION_KEY_BROKER": "@microcredito-staging/agente",
"port": 9801, "SESSION_KEY_CUSTOMER": "@microcredito-staging/cliente"
"module": 1,
"version": 3
},
"accessToken": "ab4dfc4ab84517f900193f8a2530680c73ad3539f75e1f5661bee7065cfcdd32",
"resources": {
"get": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/getpr",
"put": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/putpr"
},
"logs": true
} }
...@@ -90,6 +90,7 @@ ...@@ -90,6 +90,7 @@
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "4.0.0-alpha.58", "@material-ui/lab": "4.0.0-alpha.58",
"@material-ui/pickers": "^3.3.10", "@material-ui/pickers": "^3.3.10",
"@microcredito/client": "^0.7.11",
"@reduxjs/toolkit": "^1.2.5", "@reduxjs/toolkit": "^1.2.5",
"@types/react-swipeable-views": "^0.13.1", "@types/react-swipeable-views": "^0.13.1",
"@types/react-swipeable-views-utils": "^0.13.3", "@types/react-swipeable-views-utils": "^0.13.3",
......
{ {
"service": { "API_URL": "http://172.16.17.3:8080",
"url": "https://srvd1.dev.evologica.com.br/cxClient/cxIsapiClient.dll/gatewayJSON?version=4", "APP_NAME_BROKER": "@microcredito-dev/agente",
"server": "192.168.0.34", "APP_NAME_CUSTOMER": "@microcredito-dev/cliente",
"system": 19, "SESSION_KEY_BROKER": "@microcredito-dev/agente",
"port": 9801, "SESSION_KEY_CUSTOMER": "@microcredito-dev/cliente"
"module": 1,
"version": 3
},
"accessToken": "ab4dfc4ab84517f900193f8a2530680c73ad3539f75e1f5661bee7065cfcdd32",
"resources": {
"get": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/getpr",
"put": "https://mdk.dev.evologica.com.br/cxClient/cxIsapiClient.dll/putpr"
},
"logs": true
} }
\ No newline at end of file
...@@ -8,6 +8,6 @@ server.get('*', (req, res) => { ...@@ -8,6 +8,6 @@ server.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'dist', 'index.html')) res.sendFile(path.join(__dirname, 'dist', 'index.html'))
}) })
server.listen(3010, () => { server.listen(3000, () => {
console.log('Listening at http://localhost:3010/') console.log('Listening at http://localhost:3000/')
}) })
import { Entity } from '@agiliza/utils/method'
import { Customer } from '../'
export interface City extends Entity {
name: string
state: string
}
export interface State extends Entity {
name: string
acronym: string
}
export interface AuthenticationContext {
states: State[]
cities: City[]
}
type OutputGetContext = AuthenticationContext
export interface GetContext {
Input: void
Output: OutputGetContext
}
interface InputVerifyCPF {
cpf: string
}
export interface VerifyCPF {
Input: InputVerifyCPF
Output: void
}
interface InputCreateCustomer {
cpf: string
name: string
email: string
phone: string
cep: string
street: string
number: string
complement: string
district: string
state: string
city: string
}
interface OutputCreateCustomer {
customer: Customer
}
export interface CreateCustomer {
Input: InputCreateCustomer
Output: OutputCreateCustomer
}
interface InputSendCode {
cpf: string
email: string
}
export interface SendCode {
Input: InputSendCode
Output: void
}
interface InputVerifyCode {
code: string
cpf: string
}
export interface VerifyCode {
Input: InputVerifyCode
Output: void
}
interface InputCreatePassword {
password: string
code: string
cpf: string
}
export interface CreatePassword {
Input: InputCreatePassword
Output: void
}
export interface GetLoggedCustomer {
Input: void
Output: Customer
}
import { Entity } from '@agiliza/utils/method'
export interface City extends Entity {
name: string
state: string
}
export interface State extends Entity {
name: string
acronym: string
}
import { ErrorTypes } from '@agiliza/constants/error'
interface Error {
type: ErrorTypes
omit?: boolean
}
// Usuário não está autorizado (Login)
export interface UnauthorizedError extends Error {
type: ErrorTypes.UNAUTHORIZED
message?: string
}
// Token de usuário expirou
export interface ExpiredSessionError extends Error {
type: ErrorTypes.EXPIRED_SESSION
message: string
}
// Validação de campos (422, resposta padrão para esse status)
export interface InvalidFields {
[field: string]: string
}
export interface FormValidationError extends Error {
type: ErrorTypes.FORM_VALIDATION
invalidFields: InvalidFields
detail: string
}
// Campo ausente do requerido na API (não deve aparecer normalmente)
export interface MissingInputError extends Error {
type: ErrorTypes.MISSING_INPUT
missingFields: string[]
detail: string
}
// Erros de servidor (500) e quais outros erros não mapeados
export interface InternalServerError extends Error {
type: ErrorTypes.INTERNAL_SERVER
message: string
stack: string
}
// Código de Primeiro Acesso/Esqueci a Senha é inválido
export interface InvalidVerificationCodeError extends Error {
type: ErrorTypes.INVALID_EMAIL_CODE
message: string
}
// Usuário já existe disponível no sistema (Primeiro Acesso)
export interface AlreadyEnabledError extends Error {
type: ErrorTypes.ALREADY_ENABLED
message?: string
}
// Usuário não encontrado (404)
export interface UserNotFoundError extends Error {
type: ErrorTypes.USER_NOT_FOUND
message?: string
}
// Usuário não pode agendar uma nova visita
export interface ScheduleForbiddenError extends Error {
type: ErrorTypes.SCHEDULE_FORBIDDEN
message?: string
}
export type ApiError =
| UnauthorizedError
| ExpiredSessionError
| FormValidationError
| InternalServerError
| MissingInputError
| InvalidVerificationCodeError
| AlreadyEnabledError
| UserNotFoundError
| ScheduleForbiddenError
export * from './proposalData'
export * from './context'
export * from './error'
export * from './simulation'
export * from './authentication'
export interface Customer {
name: string
email: string
ownerName?: string
ownerCPF?: string
cpfcnpj: string
phone: string
avatarUrl?: string
address?: Partial<Address>
profilePicture?: string
}
export * from './proposalData'
import { Entity } from '@agiliza/utils/method'
export interface SimulationCategory extends Entity {
description: string
fullDescription: string
maxInstallment: number
maxGraceMonths: number
}
export interface SimulationContext {
gracePeriods: CreditGracePeriod[]
installments: CreditInstallment[]
}
//Prestação
export interface CreditInstallment extends Entity {
description: string
}
// Carência
export interface CreditGracePeriod extends Entity {
description: string
}
export * from './context'
export * from './simulation'
import { Entity } from '@agiliza/utils/method'
export interface SubProduct extends Entity {
description?: string
fee?: number
IOF?: number
maxAmountInstallment?: number
installementOptions: InstallmentOption[]
TAC?: number
}
export interface GetSubProducts {
Input: InputGetSubProducts
Output: OutputGetSubProducts
}
interface InputGetSubProducts {
creditValue: number
idModality: string
idGracePeriod: string
installmentsNumber: number
}
type OutputGetSubProducts = SubProduct[]
export interface InstallmentOption extends Entity {
installmentAmount?: number
installmentValue?: number
netValue?: number
}
import { getSessionManager } from '@agiliza/curio'
import { System as ApiSystem } from '../interfaces/System'
import { DYNAMO_TRANSITIONS } from '../useCases/transitions'
export interface SearchParams {
values: Record<string, any>
}
export function createMenuAPI() {
return {
fetchMenu: async function () {
const sessionManger = await getSessionManager()
const response = await sessionManger.session!.sendRequest(DYNAMO_TRANSITIONS.menu, {
Level: {
_: sessionManger.session!.module,
},
Version: {
_: sessionManger.session!.version,
},
})
return response.System as ApiSystem
},
}
}
interface JavaConfig {
API_URL: string
APP_NAME_BROKER: string
APP_NAME_CUSTOMER: string
SESSION_KEY_BROKER: string
SESSION_KEY_CUSTOMER: string
}
const JAVA_CONFIG_PATH = './config.json'
let javaConfig: JavaConfig
export const getJavaConfig = async () => {
if (!javaConfig) {
try {
javaConfig = await (await fetch(JAVA_CONFIG_PATH)).json()
return javaConfig
} catch (e) {
if (e instanceof Response) {
//Not found, most likely
throw process.env.NODE_ENV === 'production'
? 'Não foi encontrado o arquivo config.json na raiz da aplicação.'
: 'Arquivo config.json faltando na pasta public.'
}
throw e
}
}
}
import { CreateCustomer } from '@agiliza/api/domain'
import { extractNumbers } from '@agiliza/utils/extractors'
import { CriarUsuarioPrimeiroAcessoRequest } from '@microcredito/client/dist/apis/ClienteApi'
import { ApiAdapter } from './shared'
export class CreateCustomerApiMapper implements ApiAdapter<CreateCustomer['Input'], CriarUsuarioPrimeiroAcessoRequest> {
public mapDomainToApiModel = (input: CreateCustomer['Input']): CriarUsuarioPrimeiroAcessoRequest => ({
criarUsuarioPrimeiroAcessoRequestApiModel: {
usuario: {
usuarioCliente: {
cpfcnpj: extractNumbers(input.cpf),
nome: input.name,
email: input.email,
celular: extractNumbers(input.phone),
endereco: {
cep: extractNumbers(input.cep),
logradouro: input.street,
numero: input.number,
estado: input.state,
municipio: input.city,
complemento: input.complement,
bairro: input.district,
},
},
},
},
})
}
import * as AuthenticationApiMappers from './authentication'
import * as SessionApiAdapters from './session'
import * as SimulationApiMappers from './simulation'
export { SessionApiAdapters, SimulationApiMappers, AuthenticationApiMappers }
import { ClienteApiModels } from '@microcredito/client'
import { ApiAdapter } from './shared'
interface LoginParams {
username: string
password: string
}
export class LoginApiAdapter implements ApiAdapter<LoginParams, ClienteApiModels.LoginRequest> {
public mapDomainToApiModel = (params: LoginParams): ClienteApiModels.LoginRequest => {
const result: ClienteApiModels.LoginRequest = {
loginRequestApiModel: {
login: params.username,
senha: params.password,
agente: false,
},
}
return result
}
}
export interface ApiAdapter<A, B> {
mapDomainToApiModel(domain: A): B
}
import { GetSubProducts } from '@agiliza/api/domain/simulation/simulation'
import { SimularCreditoRequestApiModel } from '@microcredito/client'
import { ApiAdapter } from '../shared'
export class GetSubProductsApiMapper implements ApiAdapter<GetSubProducts['Input'], SimularCreditoRequestApiModel> {
public mapDomainToApiModel = (input: GetSubProducts['Input']): SimularCreditoRequestApiModel => ({
valorCredito: input.creditValue,
codigoCarencia: input.idGracePeriod,
codigoModalidade: input.idModality,
numeroParcelas: input.installmentsNumber,
})
}
import { EstadoApiModel, MunicipioApiModel } from '@microcredito/client'
import { City, State } from '../../domain'
import { DomainAdapter } from './shared'
export class CityMapper implements DomainAdapter<MunicipioApiModel, City> {
public mapApiModelToDomain = (apimodel: MunicipioApiModel): City => {
return {
id: apimodel.codigo,
name: apimodel.nome,
state: apimodel.estado,
}
}
}
export class StateMapper implements DomainAdapter<EstadoApiModel, State> {
public mapApiModelToDomain = (apimodel: EstadoApiModel): State => {
return {
id: apimodel.codigo,
name: apimodel.nome,
acronym: apimodel.sigla,
}
}
}
import { EstadoApiModel, MunicipioApiModel } from '@microcredito/client'
import { City, State } from '../../domain'
import { DomainAdapter } from './shared'
export class CityAdapter implements DomainAdapter<MunicipioApiModel, City> {
public mapApiModelToDomain = (apimodel: MunicipioApiModel): City => {
return {
id: apimodel.codigo,
name: apimodel.nome,
state: apimodel.estado,
}
}
}
export class StateAdapter implements DomainAdapter<EstadoApiModel, State> {
public mapApiModelToDomain = (apimodel: EstadoApiModel): State => {
return {
id: apimodel.codigo,
name: apimodel.nome,
acronym: apimodel.sigla,
}
}
}
import { Customer } from '@agiliza/api/domain'
import { UsuarioApiModel } from '@microcredito/client'
import { DomainAdapter } from './shared'
export class CustomerMapper implements DomainAdapter<UsuarioApiModel, Customer> {
public mapApiModelToDomain = ({ usuarioCliente }: UsuarioApiModel): Customer => ({
profilePicture: usuarioCliente?.fotoPerfil,
cpfcnpj: usuarioCliente?.cpfcnpj || '',
name: usuarioCliente?.nome || '',
phone: usuarioCliente?.celular || '',
ownerCPF: usuarioCliente?.cpfResponsavel,
ownerName: usuarioCliente?.nomeResponsavel,
address: usuarioCliente?.endereco
? {
additionalInfo: usuarioCliente?.endereco.complemento,
cep: usuarioCliente?.endereco.cep,
city: usuarioCliente?.endereco.municipio,
state: usuarioCliente?.endereco.estado,
district: usuarioCliente?.endereco.bairro,
number: usuarioCliente?.endereco.numero,
street: usuarioCliente?.endereco.logradouro,
id: usuarioCliente?.endereco.id,
}
: undefined,
email: usuarioCliente?.email || '',
})
}
import { ApiError, InternalServerError } from '@agiliza/api/domain'
import { ErrorTypes } from '@agiliza/constants/error'
import { DomainAdapter } from './shared'
/** Default response error adapter */
export class ResponseErrorAdapter implements DomainAdapter<Response, Promise<ApiError>> {
public async mapApiModelToDomain(error: Response): Promise<ApiError> {
let message: string
console.log('ERROR', error)
if (error instanceof Error) {
console.log(error)
if (error instanceof TypeError && error.message.includes('Network')) {
// Network error
message = 'Ocorreu um problema na conexão ao servidor. Verifique se você está conectado à rede.'
} else {
message = error.message
}
} else {
try {
// console.log('Request:', error)
const body = await error.json()
console.log(body)
if (body.message) {
message = body.message
} else if (body.erros) {
message = body.erros[0].motivo
} else {
if (body.error)
if (Array.isArray(body.error)) message = body.error[0].motivo
else if (body.status && body.status === 404) message = `${body.error}\nPath: ${body.path}`
else message = body.error
else message = body.toString()
}
} catch (e) {
// console.log(error)
message = error.statusText
}
}
return {
type: ErrorTypes.INTERNAL_SERVER,
message: message || 'Ocorreu um problema na requisição ao servidor.',
} as InternalServerError
}
}
/** Default authenticated response error adapter */
export class AuthResponseErrorAdapter extends ResponseErrorAdapter {
public async mapApiModelToDomain(error: Response): Promise<ApiError> {
// if (error instanceof Response && error.status === 403) {
// return {
// type: ErrorTypes.EXPIRED_SESSION,
// message: 'A sua sessão expirou.',
// }
// }
return super.mapApiModelToDomain(error)
}
}
// export class ValidateTokenErrorAdapter extends ResponseErrorAdapter {
// public mapApiModelToDomain = (error: Response): ApiError | undefined => {
// if (error instanceof Response && error.status === 401) {
// return {
// type: ErrorTypes.INVALID_EMAIL_CODE,
// message: 'Código de verificação inválido.',
// }
// }
// }
// }
import * as AuthenticationMappers from './authentication'
import * as ContextMappers from './context'
import * as CustomerMappers from './customer'
import * as ErrorMappers from './error'
import * as SessionAdapters from './session'
import * as SimulationMappers from './simulation'
export { ContextMappers, ErrorMappers, SessionAdapters, SimulationMappers, AuthenticationMappers, CustomerMappers }
import type { ApiError } from '@agiliza/api/domain'
import { ErrorTypes } from '@agiliza/constants/error'
import { ResponseErrorAdapter } from './error'
export class LoginErrorAdapter extends ResponseErrorAdapter {
public mapApiModelToDomain = async (error: Response): Promise<ApiError> => {
if (error instanceof Response) {
if (error.status === 403) {
// Unauthorized
return {
type: ErrorTypes.UNAUTHORIZED,
message: 'As credenciais fornecidas não são válidas.',
}
}
}
return super.mapApiModelToDomain(error)
}
}
export interface DomainAdapter<A, B> {
mapApiModelToDomain(apiModel: A): B
}
import { SimulationCategory } from '@agiliza/api/domain'
import { CategoriaSimulacaoApiModel } from '@microcredito/client'
import { DomainAdapter } from '../shared'
export class SimulationCategoryMapper implements DomainAdapter<CategoriaSimulacaoApiModel, SimulationCategory> {
public mapApiModelToDomain = (apimodel: CategoriaSimulacaoApiModel): SimulationCategory => {
return {
id: apimodel.id || '',
description: apimodel.descricao || '',
fullDescription: apimodel.descricaoLonga || '',
maxInstallment: apimodel.numeroMaximoParcelas,
maxGraceMonths: apimodel.carenciaMaxima,
}
}
}
export * from './context'
export * from './simulation'
import { InstallmentOption, SubProduct } from '@agiliza/api/domain'
import { OpcaoPrestacaoApiModel, SubprodutoApiModel } from '@microcredito/client'
import { DomainAdapter } from '../shared'
export class SubProductMapper implements DomainAdapter<SubprodutoApiModel, SubProduct> {
public mapInstallmentOption = (opcaoPrestacao: OpcaoPrestacaoApiModel): InstallmentOption => ({
id: opcaoPrestacao.id || '',
installmentAmount: opcaoPrestacao.quantidadePrestacoes,
installmentValue: opcaoPrestacao.valor,
netValue: opcaoPrestacao.valorLiquido,
})
public mapApiModelToDomain = (subproduto: SubprodutoApiModel): SubProduct => ({
id: subproduto.id || '',
description: subproduto.descricao,
IOF: subproduto.iof,
TAC: subproduto.tac,
fee: subproduto.taxa,
installementOptions: subproduto.opcoesPrestacao?.map(this.mapInstallmentOption) || [],
maxAmountInstallment: subproduto.qntMaxPrestacoes,
})
}
import * as error from './error' import * as error from './error'
export * from './domain'
export * from './api'
export { error } export { error }
import {
CreateCustomer,
CreatePassword,
GetContext,
GetLoggedCustomer,
SendCode,
VerifyCode,
VerifyCPF
} from '@agiliza/api/domain'
import { extractNumbers } from '@agiliza/utils/extractors'
import { ClienteApi, Configuration, DominioApi } from '@microcredito/client'
import {
AuthenticationApiMappers,
AuthenticationMappers,
CustomerMappers,
ErrorMappers
} from '../mappers'
import { API_URL, mapUserAgentToString, UserAgent } from './shared'
export interface AuthenticationRepository {
getContext(): Promise<GetContext['Output']>
verifyCPF(input: VerifyCPF['Input']): Promise<VerifyCPF['Output']>
createCustomer(input: CreateCustomer['Input']): Promise<CreateCustomer['Output']>
sendCode(input: SendCode['Input']): Promise<SendCode['Output']>
verifyCode(input: VerifyCode['Input']): Promise<VerifyCode['Output']>
createPassword(input: CreatePassword['Input']): Promise<CreatePassword['Output']>
getLoggedCustomer(): Promise<GetLoggedCustomer['Output']>
}
export class AuthenticationRepositoryImpl implements AuthenticationRepository {
private api: DominioApi
private clienteApi: ClienteApi
private errorAdapter: ErrorMappers.ResponseErrorAdapter
private stateMapper: AuthenticationMappers.StateMapper
private cityMapper: AuthenticationMappers.CityMapper
private customerMapper: CustomerMappers.CustomerMapper
private authenticationApiMapper: AuthenticationApiMappers.CreateCustomerApiMapper
constructor(userAgent: string) {
this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
this.stateMapper = new AuthenticationMappers.StateMapper()
this.cityMapper = new AuthenticationMappers.CityMapper()
this.customerMapper = new CustomerMappers.CustomerMapper()
this.authenticationApiMapper = new AuthenticationApiMappers.CreateCustomerApiMapper()
this.api = new DominioApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
this.clienteApi = new ClienteApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
}
public getContext = async (): Promise<GetContext['Output']> => {
try {
const estados = await this.api.obterEstados()
const cidades = await this.api.obterMunicipios()
return { states: estados.map(this.stateMapper.mapApiModelToDomain), cities: cidades.map(this.cityMapper.mapApiModelToDomain) }
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public verifyCPF = async (input: VerifyCPF['Input']): Promise<VerifyCPF['Output']> => {
try {
await this.clienteApi.iniciarPrimeiroAcessoCliente({ iniciarPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf) } })
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public createCustomer = async (input: CreateCustomer['Input']): Promise<CreateCustomer['Output']> => {
try {
const cliente = await this.clienteApi.criarUsuarioPrimeiroAcesso(this.authenticationApiMapper.mapDomainToApiModel(input))
return { customer: this.customerMapper.mapApiModelToDomain(cliente) }
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public sendCode = async (input: SendCode['Input']): Promise<SendCode['Output']> => {
try {
await this.clienteApi.enviarCodigoPrimeiroAcessoCliente({
enviarCodigoPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), email: input.email },
})
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public verifyCode = async (input: VerifyCode['Input']): Promise<VerifyCode['Output']> => {
try {
await this.clienteApi.validarCodigoPrimeiroAcessoCliente({
validarCodigoPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), codigo: extractNumbers(input.code) },
})
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public createPassword = async (input: CreatePassword['Input']): Promise<CreatePassword['Output']> => {
try {
await this.clienteApi.definirSenhaPrimeiroAcessoCliente({
definirSenhaPrimeiroAcessoRequestApiModel: { cpfcnpj: extractNumbers(input.cpf), codigo: extractNumbers(input.code), senha: input.password },
})
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public getLoggedCustomer = async (): Promise<GetLoggedCustomer['Output']> => {
try {
const usuario = await this.clienteApi.obterUsuarioLogado()
return this.customerMapper.mapApiModelToDomain(usuario)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
}
export class AuthenticationRepositoryImplFactory {
static create(userAgent: UserAgent) {
const repository = new AuthenticationRepositoryImpl(mapUserAgentToString(userAgent))
return repository
}
}
import { Configuration, DominioApi } from '@microcredito/client'
import { City, State } from '../domain'
import { ContextMappers, ErrorMappers } from '../mappers'
import { API_URL, mapUserAgentToString, UserAgent } from './shared'
export interface ContextRepository {
fetchCities(): Promise<City[]>
fetchStates(): Promise<State[]>
}
export class ContextRepositoryImpl implements ContextRepository {
private api: DominioApi
private stateAdapter: ContextMappers.StateAdapter
private cityAdapter: ContextMappers.CityAdapter
private errorAdapter: ErrorMappers.ResponseErrorAdapter
constructor(userAgent: string) {
this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
this.stateAdapter = new ContextMappers.StateAdapter()
this.cityAdapter = new ContextMappers.CityAdapter()
this.api = new DominioApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
}
public fetchCities = async (): Promise<City[]> => {
try {
const result = await this.api.obterMunicipios()
return result.map(this.cityAdapter.mapApiModelToDomain)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public fetchStates = async (): Promise<State[]> => {
try {
const result = await this.api.obterEstados()
return result.map(this.stateAdapter.mapApiModelToDomain)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
}
export class ContextRepositoryImplFactory {
static create(userAgent: UserAgent) {
const repository = new ContextRepositoryImpl(mapUserAgentToString(userAgent))
return repository
}
}
import pMinDelay from 'p-min-delay' export * from './session'
export * from './simulation'
import * as apiSession from './session' export * from './authentication'
type ApiSessionManager<A> = (...args: any[]) => Promise<A | void>
interface ApiMapObject<A> {
[key: string]: ApiSessionManager<A>
}
export const applyDelayToApi = <
A,
M extends ApiMapObject<A> | ApiSessionManager<A>
>(
apiMapObject: M,
ms = 600
): M =>
Object.entries(apiMapObject).reduce(
(acc, [k, v]: [string, ApiSessionManager<any>]) => ({
...acc,
[k]: (...args: any[]) => pMinDelay(v(...args), ms)
}),
{} as M
)
export const session = applyDelayToApi(apiSession)
import { getSessionManager } from '@agiliza/curio' import { extractNumbers } from '@agiliza/utils/extractors'
import { ClienteApi, Configuration } from '@microcredito/client'
import { createMenuAPI } from '../dynamo' import { Customer } from '../domain'
import { CustomerMappers, SessionAdapters, SessionApiAdapters as Adapters } from '../mappers'
import { API_URL, mapUserAgentToString, SESSION_KEY, UserAgent } from './shared'
// import { getSessionManager } from '@agiliza/curio'
// import { createMenuAPI } from '../dynamo' // import { createMenuAPI } from '../dynamo'
// import { SolicitarRedefinicao, VerificarCodigo, RedefinirSenha } from '../interfaces/Login' // // import { createMenuAPI } from '../dynamo'
// const ucRecuperarSenha = { // // import { SolicitarRedefinicao, VerificarCodigo, RedefinirSenha } from '../interfaces/Login'
// id: '3522',
// SOLICITAR_REDEFINICAO: 'RM_SOLICITAR_REDEFINICAO',
// VERIFICAR_CODIGO: 'RM_VERIFICAR_CODIGO',
// DEFIINIR_SENHA: 'RM_DEFINIR_SENHA'
// }
interface LoginParams { // // const ucRecuperarSenha = {
username: string // // id: '3522',
password: string // // SOLICITAR_REDEFINICAO: 'RM_SOLICITAR_REDEFINICAO',
} // // VERIFICAR_CODIGO: 'RM_VERIFICAR_CODIGO',
// // DEFIINIR_SENHA: 'RM_DEFINIR_SENHA'
// // }
export const initialize = async () => { // interface LoginParams {
await getSessionManager() // username: string
} // password: string
// }
export const login = async ({ username, password }: LoginParams) => { // export const initialize = async () => {
const sessionManager = await getSessionManager() // await getSessionManager()
return sessionManager.openMainUseCase(username, password) // }
}
export const logout = async () => { // export const login = async ({ username, password }: LoginParams) => {
const sessionManager = await getSessionManager() // const sessionManager = await getSessionManager()
sessionManager?.session?.abort() // return sessionManager.openMainUseCase(username, password)
} // }
// export const logout = async () => {
// const sessionManager = await getSessionManager()
// sessionManager?.session?.abort()
// }
export const { fetchMenu } = createMenuAPI() // export const { fetchMenu } = createMenuAPI()
// export const solicitarRedefinicao = async (funcionario: SolicitarRedefinicao) => { // export const solicitarRedefinicao = async (funcionario: SolicitarRedefinicao) => {
// const mainUseCase = await sessionManager.anonymousConnection() // const mainUseCase = await sessionManager.anonymousConnection()
...@@ -72,3 +78,70 @@ export const { fetchMenu } = createMenuAPI() ...@@ -72,3 +78,70 @@ export const { fetchMenu } = createMenuAPI()
// enviarCodigoSenha, // enviarCodigoSenha,
// redefinirSenha // redefinirSenha
// } // }
export interface SessionRepository {
login(username: string, password: string): Promise<Customer>
logout(): void
connect(): void
}
export class SessionRepositoryImpl implements SessionRepository {
private customer: string
private api: ClienteApi
private loginApiAdapter: Adapters.LoginApiAdapter
private errorAdapter: SessionAdapters.LoginErrorAdapter
private customerMapper: CustomerMappers.CustomerMapper
constructor(userAgent: string) {
this.customer = userAgent
this.errorAdapter = new SessionAdapters.LoginErrorAdapter()
this.loginApiAdapter = new Adapters.LoginApiAdapter()
this.customerMapper = new CustomerMappers.CustomerMapper()
this.api = new ClienteApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
}
public login = async (username: string, password: string) => {
try {
const params = this.loginApiAdapter.mapDomainToApiModel({ username: extractNumbers(username), password })
const accessToken = await this.api.login(params)
const clienteApi = new ClienteApi(
new Configuration({
basePath: API_URL,
accessToken: accessToken.token,
headers: {
'User-Agent': this.customer,
},
})
)
const cliente = await clienteApi.obterUsuarioLogado()
return this.customerMapper.mapApiModelToDomain(cliente)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
public logout = () => {
localStorage.removeItem(SESSION_KEY)
}
public connect = () => {
const sessionKey = localStorage.getItem(SESSION_KEY)
if (sessionKey) return
else throw new Error()
// return await new Promise<void>((res, rej) => {
// })
}
}
export class SessionRepositoryImplFactory {
static create(userAgent: UserAgent) {
const repository = new SessionRepositoryImpl(mapUserAgentToString(userAgent))
return repository
}
}
const config = await fetch('./config.json').then((res) => res.json())
const { API_URL, APP_NAME_CUSTOMER, SESSION_KEY_CUSTOMER } = config
export { API_URL }
export const SESSION_KEY = SESSION_KEY_CUSTOMER
export const APP_NAME = APP_NAME_CUSTOMER
export interface UserAgent {
appVersion: string
platform: {
name: string
version: string
}
}
export function mapUserAgentToString(agent: UserAgent) {
return `${APP_NAME}/${agent.appVersion} (${agent.platform.name}/${agent.platform.version})`
}
import { SimulationCategory } from '@agiliza/api/domain'
import { Configuration, DominioApi } from '@microcredito/client'
import { ErrorMappers, SimulationMappers } from '../../mappers'
import { API_URL, mapUserAgentToString, UserAgent } from '../shared'
export interface SimulationContextRepository {
fetchSimulationCategories(): Promise<SimulationCategory[]>
}
export class SimulationContextRepositoryImpl implements SimulationContextRepository {
private api: DominioApi
private simulationCategoryMapper: SimulationMappers.SimulationCategoryMapper
private errorAdapter: ErrorMappers.ResponseErrorAdapter
constructor(userAgent: string) {
this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
this.simulationCategoryMapper = new SimulationMappers.SimulationCategoryMapper()
this.api = new DominioApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
}
public fetchSimulationCategories = async (): Promise<SimulationCategory[]> => {
try {
const result = await this.api.obterCategoriasSimulacao()
return result.map(this.simulationCategoryMapper.mapApiModelToDomain)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
}
export class SimulationContextRepositoryImplFactory {
static create(userAgent: UserAgent) {
const repository = new SimulationContextRepositoryImpl(mapUserAgentToString(userAgent))
return repository
}
}
import { GetSubProducts } from '@agiliza/api/domain/simulation/simulation'
import { Configuration } from '@microcredito/client'
import { AnonimoApi } from '@microcredito/client/dist/apis/AnonimoApi'
import { ErrorMappers, SimulationApiMappers, SimulationMappers } from '../../mappers'
import { API_URL, mapUserAgentToString, UserAgent } from '../shared'
export interface SimulationRepository {
getSubproducts(input: GetSubProducts['Input']): Promise<GetSubProducts['Output']>
}
export class SimulationRepositoryImpl implements SimulationRepository {
private api: AnonimoApi
private getSubproductsApiMapper: SimulationApiMappers.GetSubProductsApiMapper
private subproductMapper: SimulationMappers.SubProductMapper
private errorAdapter: ErrorMappers.ResponseErrorAdapter
constructor(userAgent: string) {
this.errorAdapter = new ErrorMappers.ResponseErrorAdapter()
this.getSubproductsApiMapper = new SimulationApiMappers.GetSubProductsApiMapper()
this.subproductMapper = new SimulationMappers.SubProductMapper()
this.api = new AnonimoApi(
new Configuration({
basePath: API_URL,
headers: {
'User-Agent': userAgent,
},
})
)
}
public getSubproducts = async (input: GetSubProducts['Input']): Promise<GetSubProducts['Output']> => {
try {
const result = await this.api.anonimoObterSubprodutos({
simularCreditoRequestApiModel: this.getSubproductsApiMapper.mapDomainToApiModel(input),
})
return result.map(this.subproductMapper.mapApiModelToDomain)
} catch (e) {
const result = await this.errorAdapter.mapApiModelToDomain(e)
throw result
}
}
}
export class SimulationRepositoryImplFactory {
static create(userAgent: UserAgent) {
const repository = new SimulationRepositoryImpl(mapUserAgentToString(userAgent))
return repository
}
}
...@@ -7,23 +7,16 @@ import { withStyles, WithStyles } from '@material-ui/core/styles' ...@@ -7,23 +7,16 @@ import { withStyles, WithStyles } from '@material-ui/core/styles'
import { styles } from './styles' import { styles } from './styles'
type ExtendedTypes = WithStyles<typeof styles> & type ExtendedTypes = WithStyles<typeof styles> &
Pick<ButtonProps, 'disabled' | 'onClick' | 'type' | 'children' | 'className' | 'color'> Pick<ButtonProps, 'disabled' | 'onClick' | 'type' | 'children' | 'className' | 'color' | 'size' | 'variant'>
interface Props extends ExtendedTypes { interface Props extends ExtendedTypes {
fetching?: boolean fetching?: boolean
} }
const ButtonWithProgress = (props: Props) => { const ButtonWithProgress = (props: Props) => {
const { className, classes, fetching, disabled, onClick, type, children, color = 'primary' } = props const { className, classes, fetching, disabled, onClick, type, children, color = 'primary', size, variant = 'contained' } = props
return ( return (
<Button <Button className={className} disabled={disabled || fetching} onClick={onClick} variant={variant} color={color} type={type} size={size}>
className={className}
disabled={disabled || fetching}
onClick={onClick}
variant="contained"
color={color}
type={type}
>
{fetching && <LinearProgress className={classes.progress} />} {fetching && <LinearProgress className={classes.progress} />}
{children} {children}
</Button> </Button>
......
...@@ -9,7 +9,7 @@ import withStyles, { WithStyles } from '@material-ui/styles/withStyles' ...@@ -9,7 +9,7 @@ import withStyles, { WithStyles } from '@material-ui/styles/withStyles'
import styles from './styles' import styles from './styles'
type BaseProps = WithStyles<typeof styles> & type BaseProps = WithStyles<typeof styles> &
Pick<TextFieldProps, 'value' | 'onChange' | 'required' | 'label' | 'type' | 'className' | 'InputProps' | 'autoFocus'> Pick<TextFieldProps, 'value' | 'onChange' | 'required' | 'label' | 'type' | 'className' | 'InputProps' | 'autoFocus' | 'inputProps'>
interface Props extends BaseProps {} interface Props extends BaseProps {}
...@@ -30,11 +30,7 @@ const TextFieldWithIcon = (props: Props) => { ...@@ -30,11 +30,7 @@ const TextFieldWithIcon = (props: Props) => {
InputProps || { InputProps || {
startAdornment: startAdornment:
type === 'password' ? ( type === 'password' ? (
<InputAdornment <InputAdornment position="start" onClick={() => setIsPasswordVisible(!isPasswordVisible)} className={classes.inputAdorment}>
position="start"
onClick={() => setIsPasswordVisible(!isPasswordVisible)}
className={classes.inputAdorment}
>
{isPasswordVisible ? <Visibility /> : <VisibilityOff />} {isPasswordVisible ? <Visibility /> : <VisibilityOff />}
</InputAdornment> </InputAdornment>
) : null, ) : null,
......
...@@ -3,7 +3,7 @@ import React from 'react' ...@@ -3,7 +3,7 @@ import React from 'react'
import { RouteComponentProps } from 'react-router' import { RouteComponentProps } from 'react-router'
import logo from '@agiliza/public/images/logo.svg' import logo from '@agiliza/public/images/logo.svg'
import { actions as sessionActions } from '@agiliza/redux/session' import { actions as loginActions } from '@agiliza/redux/ui/login'
import Divider from '@material-ui/core/Divider' import Divider from '@material-ui/core/Divider'
import MUIDrawer from '@material-ui/core/Drawer' import MUIDrawer from '@material-ui/core/Drawer'
...@@ -22,7 +22,7 @@ type BaseProps = RouteComponentProps ...@@ -22,7 +22,7 @@ type BaseProps = RouteComponentProps
export interface Props extends BaseProps { export interface Props extends BaseProps {
drawerOpen: boolean drawerOpen: boolean
toggleDrawer: () => void toggleDrawer: () => void
logout: typeof sessionActions.logout logout: typeof loginActions.logout
Items?: React.ComponentType<DrawerItemsProps> Items?: React.ComponentType<DrawerItemsProps>
} }
......
import React, { useEffect, useState } from 'react' import React, { useState } from 'react'
import { ConfigService } from '@agiliza/curio/SessionManager'
import Popover from '@material-ui/core/Popover' import Popover from '@material-ui/core/Popover'
import Typography from '@material-ui/core/Typography' import Typography from '@material-ui/core/Typography'
import InfoIcon from '@material-ui/icons/InfoRounded' import InfoIcon from '@material-ui/icons/InfoRounded'
...@@ -8,9 +7,7 @@ import { WithStyles, withStyles } from '@material-ui/styles' ...@@ -8,9 +7,7 @@ import { WithStyles, withStyles } from '@material-ui/styles'
import { styles } from './styles' import { styles } from './styles'
interface Props extends WithStyles<typeof styles> { interface Props extends WithStyles<typeof styles> {}
service?: ConfigService
}
interface PopItem { interface PopItem {
key: string key: string
...@@ -19,22 +16,22 @@ interface PopItem { ...@@ -19,22 +16,22 @@ interface PopItem {
} }
const PopoverVersion = (props: Props) => { const PopoverVersion = (props: Props) => {
const { classes, service } = props const { classes } = props
const [popItems, setPopItems] = useState<PopItem[]>([]) const [popItems] = useState<PopItem[]>([])
const [anchor, setAnchor] = useState<HTMLDivElement | null>(null) const [anchor, setAnchor] = useState<HTMLDivElement | null>(null)
useEffect(() => { // useEffect(() => {
let items: PopItem[] = [{ key: 'Versão', value: VERSION }] // let items: PopItem[] = [{ key: 'Versão', value: VERSION }]
if (service) { // if (service) {
items = items.concat( // items = items.concat(
{ key: 'ISAPI', value: service?.url, underline: true }, // { key: 'ISAPI', value: service?.url, underline: true },
{ key: 'Serviço', value: service?.server }, // { key: 'Serviço', value: service?.server },
{ key: 'Porta', value: service?.port }, // { key: 'Porta', value: service?.port },
{ key: 'Sistema', value: service?.system } // { key: 'Sistema', value: service?.system }
) // )
} // }
setPopItems(items) // setPopItems(items)
}, [service]) // }, [service])
const createPopItems = (items: PopItem[]) => const createPopItems = (items: PopItem[]) =>
items.map((item, i) => ( items.map((item, i) => (
......
import React, { useMemo } from 'react'
import { City, State } from '@agiliza/api/domain'
import { ActionType } from '@agiliza/utils/hooks/state'
import { maskCEP } from '@agiliza/utils/masks'
import { SelectField, SelectFieldProps } from '@curio/components'
import { TextField, TextFieldProps } from '@material-ui/core'
import { withStyles, WithStyles } from '@material-ui/core/styles'
import { AddressState as FormState } from './state'
import styles from './styles'
type ExtendedProps = WithStyles<typeof styles>
interface Props extends ExtendedProps {
states: State[]
cities: City[]
state: FormState
actions: ActionType<FormState>
}
const Address = (props: Props) => {
const { classes, state: _state, actions, states, cities } = props
const { cep, street, number, complement, state, city, district } = _state
const handleChange =
(key: keyof FormState): TextFieldProps['onChange'] =>
(evt) => {
actions.update({ [key]: evt.target.value })
}
const handleChangeSelect =
(key: keyof FormState): SelectFieldProps['onChange'] =>
(value) => {
actions.update({ [key]: value })
}
const availableCities = useMemo(() => {
if (state === '') return []
return cities.filter((ct) => ct.state === state)
}, [state])
return (
<>
<TextField variant="outlined" label="CEP" value={maskCEP(cep)} onChange={handleChange('cep')} inputProps={{ maxLength: 9 }} />
<TextField variant="outlined" label="Rua / Avenida" value={street} onChange={handleChange('street')} />
<TextField variant="outlined" label="Número" value={number} onChange={handleChange('number')} />
<TextField variant="outlined" label="Complemento" value={complement} onChange={handleChange('complement')} />
<SelectField
id="state-select"
variant="outlined"
label="Estado"
value={state}
onChange={handleChangeSelect('state')}
items={states.map((st) => ({ label: st.name, value: st.id }))}
shrink={false}
className={classes.selectField}
/>
<SelectField
id="state-select"
variant="outlined"
disabled={!state}
label="Cidade"
value={city}
onChange={handleChangeSelect('city')}
items={availableCities.map((st) => ({ label: st.name, value: st.id }))}
shrink={false}
className={classes.selectField}
/>
<TextField variant="outlined" label="Bairro" value={district} onChange={handleChange('district')} />
</>
)
}
export default withStyles(styles)(Address)
export { default } from './Address'
export * from './Address'
export * from './state'
export interface AddressState {
cep: string
street: string
number: string
complement: string
district: string
state: string
city: string
}
export const initStateAddress: AddressState = {
cep: '',
street: '',
number: '',
complement: '',
district: '',
state: '',
city: '',
}
import { Theme } from '@material-ui/core/styles'
import { createStyles } from '@material-ui/styles'
// eslint-disable-next-line
export default (theme: Theme) =>
createStyles({
form: { display: 'flex', flexDirection: 'column' },
selectField: { marginBottom: theme.spacing(2) },
})
import React from 'react'
import { City, State } from '@agiliza/api/domain/authentication'
import { getErrorProps, useErrorValidator } from '@agiliza/utils/hooks/errorValidation'
import { ActionType } from '@agiliza/utils/hooks/state'
import { maskCPFCNPJ, maskPhone } from '@agiliza/utils/masks'
import { TextField, TextFieldProps } from '@material-ui/core'
import { withStyles, WithStyles } from '@material-ui/core/styles'
import AddressForm from '../Address'
import { initState, State as FirstAccessState } from './state'
import styles from './styles'
type ExtendedProps = WithStyles<typeof styles>
interface Props extends ExtendedProps {
states: State[]
cities: City[]
state: FirstAccessState
actions: ActionType<FirstAccessState>
}
const FirstAccess = (props: Props) => {
const { classes, states, cities, state, actions } = props
const { cpf, name, email, phone, ...addressState } = state
const { errorState, actions: errorActions } = useErrorValidator(initState)
const handleChange =
(key: keyof FirstAccessState): TextFieldProps['onChange'] =>
(evt) => {
actions.update({ [key]: evt.target.value })
errorActions.validate({ [key]: evt.target.value })
}
return (
<form className={classes.form}>
<TextField
variant="outlined"
label="CPF"
value={maskCPFCNPJ(cpf)}
onChange={handleChange('cpf')}
inputProps={{ maxLength: 14 }}
disabled
{...getErrorProps(errorState.cpf)}
/>
<TextField variant="outlined" label="Nome" value={name} onChange={handleChange('name')} />
<TextField variant="outlined" label="Email" value={email} onChange={handleChange('email')} />
<TextField variant="outlined" label="Telefone" value={maskPhone(phone)} onChange={handleChange('phone')} inputProps={{ maxLength: 15 }} />
<AddressForm state={addressState} actions={actions} states={states} cities={cities} />
</form>
)
}
export default withStyles(styles)(FirstAccess)
export { default } from './FirstAccess'
export * from './FirstAccess'
export * from './state'
export interface State {
cpf: string
name: string
email: string
phone: string
cep: string
street: string
number: string
complement: string
district: string
state: string
city: string
}
export const initState: State = {
cpf: '',
email: '',
name: '',
phone: '',
cep: '',
street: '',
number: '',
complement: '',
district: '',
state: '',
city: '',
}
import { Theme } from '@material-ui/core/styles'
import { createStyles } from '@material-ui/styles'
// eslint-disable-next-line
export default (theme: Theme) =>
createStyles({
form: {
display: 'flex',
flexDirection: 'column',
'& .MuiTextField-root': {
marginBottom: theme.spacing(2),
},
},
})
import React from 'react'
import TextFieldWithIcon from '@agiliza/components/atoms/TextFieldWithIcon'
import { InputAdornment, TextFieldProps } from '@material-ui/core'
import { withStyles, WithStyles } from '@material-ui/core/styles'
import { AccountCircle } from '@material-ui/icons'
import styles from './styles'
type ExtendedProps = WithStyles<typeof styles>
interface Props extends ExtendedProps {
username: string
password: string
onChange(type: 'username' | 'password'): TextFieldProps['onChange']
}
const Login = (props: Props) => {
const { classes, username, password, onChange } = props
return (
<form className={classes.form}>
<TextFieldWithIcon
autoFocus
required
value={username}
onChange={onChange('username')}
label="CPF"
className={classes.usernameTxtField}
inputProps={{ maxLength: 14 }}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<AccountCircle />
</InputAdornment>
),
}}
/>
<TextFieldWithIcon required value={password} onChange={onChange('password')} label="Senha" type="password" />
</form>
)
}
export default withStyles(styles)(Login)
export { default } from './Login'
export * from './Login'
import { Theme } from '@material-ui/core/styles'
import { createStyles } from '@material-ui/styles'
// eslint-disable-next-line
export default (theme: Theme) =>
createStyles({
title: {
fontSize: '150%',
fontWeight: 'bold',
overflowWrap: 'normal',
},
formContainer: {
height: '40%',
textAlign: 'center',
justifyContent: 'space-between',
display: 'flex',
flexDirection: 'column',
},
container: {
display: 'flex',
height: '100vh',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
textAlign: 'center',
},
form: {
display: 'flex',
flexDirection: 'column',
width: '100%',
margin: theme.spacing(2),
},
usernameTxtField: { marginBottom: 10 },
})
export * from './NotFound' export * from './NotFound'
export * from './AccountInputsPhoto' export * from './AccountInputsPhoto'
export * from './Login'
export * from './Address'
export * from './FirstAccess'
export enum ErrorTypes {
UNAUTHORIZED,
INTERNAL_SERVER,
FORM_VALIDATION,
MISSING_INPUT,
EXPIRED_SESSION,
INVALID_EMAIL_CODE,
ALREADY_ENABLED,
USER_NOT_FOUND,
SCHEDULE_FORBIDDEN,
}
import { UserAgent } from '@agiliza/api/useCases/shared'
import packagejson from '../../package.json'
const { version } = packagejson
export const appPlatform: UserAgent = {
appVersion: version,
platform: {
name: 'Web',
version: '1',
},
}
import { Dispatch } from 'redux'
import session from '@agiliza/redux/session'
import { MainUseCase, RequestDriver, SecurityManager, UseCaseMessageType } from '@curio/client'
import { ConnectionError, DestinataryNotFoundError } from '@curio/client/errors'
import { ABORT } from '@curio/client/utils/constants'
const STORAGE_SESSION_KEY = '@agiliza-session-token'
export interface ConfigService {
url: string
server: string
system: number
port: number
module: number
version: number
}
class Session extends MainUseCase {
module: number | undefined
version: number | undefined
constructor(token, service, driver) {
super(token, service, driver)
this.module = service.module
this.version = service.version
}
}
export class AnonymousSession extends MainUseCase {}
export class SessionManager extends SecurityManager {
public session: Session | undefined
private _dispatch: Dispatch
private _storage: Storage
private _service: ConfigService
constructor(storage: Storage, service: ConfigService, driver: RequestDriver, dispatch: Dispatch) {
super(service, driver)
this._dispatch = dispatch
this._storage = storage
this._service = service
const token = this._storage.getItem(STORAGE_SESSION_KEY)
const { actions } = session
if (process.env.NODE_ENV === 'development') {
dispatch(actions.setServiceInfo(service))
}
if (token) {
dispatch(actions.authRequest())
this.connectMainUseCase(new Session(token, this.service, driver))
this.session!.timeoutCheck()
.then(() => dispatch(actions.authSuccess()))
.catch((err) => {})
}
}
public anonymousConnection() {
return super.openMainUseCase('Administrador', '', AnonymousSession)
}
public async openMainUseCase<U extends typeof MainUseCase>(username: string, password: string) {
this.session = await super.openMainUseCase(username, password, Session)
this.session.module = this._service.module
this.session.version = this._service.version
this._storage.setItem(STORAGE_SESSION_KEY, this.session.token.toString())
this.session.addListener(ABORT, () => {
this._unauthenticate(false)
})
this.session.addListener(UseCaseMessageType.RESPONSE, (message: any) => {
if (message.error instanceof DestinataryNotFoundError || message.error instanceof ConnectionError) {
this._unauthenticate(true)
}
})
return this.session as InstanceType<U>
}
public connectMainUseCase(mainUseCase: Session) {
this.session = mainUseCase
if (!(mainUseCase instanceof AnonymousSession)) {
this._storage.setItem(STORAGE_SESSION_KEY, this.session.token.toString())
}
this.session.addListener(ABORT, () => {
this._unauthenticate(false)
})
this.session.addListener(UseCaseMessageType.RESPONSE, (message: any) => {
if (message.error instanceof DestinataryNotFoundError || message.error instanceof ConnectionError) {
this._unauthenticate(true)
}
})
return super.connectMainUseCase(mainUseCase)
}
private _unauthenticate(expired: boolean) {
this.session = undefined
this._storage.removeItem(STORAGE_SESSION_KEY)
if (expired) {
this._dispatch(session.actions.logout())
}
}
}
import { Dispatch } from 'redux'
import { store } from '@agiliza/redux'
import { FetchLink } from '@curio/client/links'
import { V3RequestParser } from '@curio/client/parsers'
import { createV3Validator } from '@curio/client/validators'
// import config from '../../config/config.json'
import { ConfigService, SessionManager } from './SessionManager'
interface Config {
service: ConfigService
accessToken: string
resources: {
get: string
put: string
}
logs: boolean
externalApiUrl: string
}
const createSessionManager = (config: Config, dispatch: Dispatch) => {
const link = new FetchLink(config.service)
const requestParser = new V3RequestParser(config.service)
const sessionManager = new SessionManager(
localStorage,
config.service,
(request) => {
return Promise.resolve(request)
.then(requestParser.parse)
.then(JSON.stringify)
.then(link.parse)
.then(link.post)
.then((response) => response.json())
.then(createV3Validator(request).validate)
},
dispatch
)
interface Attributes {
[index: string]: string
_Id: string
_Recipient: string
_Sender: string
_SerialNumber: string
_SignalName: string
}
if (config.logs) {
sessionManager[Symbol.observable]().subscribe({
next: (msg) => {
if (msg.signalName === '116' || msg.signalName === '120' || msg.signalName === '134' || !Number(msg.signalName)) {
const signalName = msg.signalName.substr(msg.signalName.indexOf('_') + 1).replace(/_/g, ' ')
const filteredBody = Object.keys(msg.body ? (msg.body as Attributes) : {}).reduce((acc, curr) => {
if (curr.indexOf('_') < 0) return { ...acc, [curr]: (msg.body as Attributes)[curr] }
else return acc
}, {})
const colorType = msg.type === 'response' ? 'green' : 'blue'
console.log(
`%c ${msg.type.toUpperCase()} %c ${signalName}`,
`color: white; font-weight: bolder; background: ${colorType}`,
'font-weight: bolder; font-style: italic;',
Object.keys(filteredBody).length !== 0 ? filteredBody : ''
)
}
},
error: console.error,
complete: () => console.log('complete'),
})
}
return sessionManager
}
const CONFIG_PATH = './config.json'
let sm: SessionManager
let config: Config
async function getSessionManager() {
if (sm) {
return sm
} else {
try {
const cnfg = await (await fetch(CONFIG_PATH)).json()
config = cnfg
sm = createSessionManager(config, store.dispatch)
return sm
} catch (e) {
if (e instanceof Response) {
//Not found, most likely
throw process.env.NODE_ENV === 'production'
? 'Não foi encontrado o arquivo config.json na raiz da aplicação.'
: 'Arquivo config.json faltando na pasta public.'
}
throw e
}
}
}
const wd: any = window
wd.__CURIO_GET_SESSION__ = () => getSessionManager().then((sm) => sm.session)
export { createSessionManager, getSessionManager, config }
import sessionEpic from './session' // import sessionEpic from './session'
import systemEpic from './system' // import systemEpic from './system'
export { systemEpic, sessionEpic } export {}
import { AnyAction } from 'redux' // import { AnyAction } from 'redux'
import { ActionsObservable, combineEpics, ofType } from 'redux-observable' // import { ActionsObservable, combineEpics, ofType } from 'redux-observable'
import { from, of } from 'rxjs' // import { from, of } from 'rxjs'
import { catchError, switchMap, tap } from 'rxjs/operators' // import { catchError, switchMap, tap } from 'rxjs/operators'
import { session } from '@agiliza/api/useCases' // import { session } from '@agiliza/api/useCases'
import { actions as sessionActions, types as sessionTypes } from '@agiliza/redux/session' // import { actions as sessionActions, types as sessionTypes } from '@agiliza/redux/session'
import { actions as loginActions, types as loginTypes } from '@agiliza/redux/ui/login' // import { actions as loginActions, types as loginTypes } from '@agiliza/redux/ui/login'
type LoginRequestAction = ReturnType<typeof loginActions.login> // type LoginRequestAction = ReturnType<typeof loginActions.login>
const loginEpic = (action$: ActionsObservable<LoginRequestAction>) => // const loginEpic = (action$: ActionsObservable<LoginRequestAction>) =>
action$.pipe( // action$.pipe(
ofType<AnyAction, LoginRequestAction>(loginTypes.login), // ofType<AnyAction, LoginRequestAction>(loginTypes.login),
switchMap((action) => // switchMap((action) =>
from(session.login(action.payload)).pipe( // from(session.login(action.payload)).pipe(
switchMap(() => { // switchMap(() => {
return of(loginActions.loginSuccess(), sessionActions.authSuccess()) // return of(loginActions.loginSuccess(), sessionActions.authSuccess())
}), // }),
catchError((error) => of(loginActions.loginError(error))) // catchError((error) => of(loginActions.loginError(error)))
) // )
) // )
) // )
type LogoutRequestAction = ReturnType<typeof sessionActions.logout> // type LogoutRequestAction = ReturnType<typeof sessionActions.logout>
const logoutEpic = (action$: ActionsObservable<AnyAction>) => // const logoutEpic = (action$: ActionsObservable<AnyAction>) =>
action$.pipe( // action$.pipe(
ofType<AnyAction, LogoutRequestAction>(sessionTypes.logout), // ofType<AnyAction, LogoutRequestAction>(sessionTypes.logout),
tap(() => void session.logout()), // tap(() => void session.logout()),
switchMap(() => of(sessionActions.logoutSuccess(), { type: 'RESET_STORE' })) // switchMap(() => of(sessionActions.logoutSuccess(), { type: 'RESET_STORE' }))
) // )
type InitializeRequestAction = ReturnType<typeof sessionActions.initialize> // type InitializeRequestAction = ReturnType<typeof sessionActions.initialize>
const initializeEpic = (action$: ActionsObservable<InitializeRequestAction>) => // const initializeEpic = (action$: ActionsObservable<InitializeRequestAction>) =>
action$.pipe( // action$.pipe(
ofType<AnyAction, InitializeRequestAction>(sessionTypes.initialize), // ofType<AnyAction, InitializeRequestAction>(sessionTypes.initialize),
switchMap(() => // switchMap(() =>
from(session.initialize()).pipe( // from(session.initialize()).pipe(
switchMap(() => { // switchMap(() => {
return of(sessionActions.initializeSuccess()) // return of(sessionActions.initializeSuccess())
}), // }),
catchError((error) => of(sessionActions.initializeError(error))) // catchError((error) => of(sessionActions.initializeError(error)))
) // )
) // )
) // )
export default combineEpics(loginEpic, logoutEpic, initializeEpic) // export default combineEpics(loginEpic, logoutEpic, initializeEpic)
import { AnyAction } from 'redux' // import { AnyAction } from 'redux'
import { ActionsObservable, combineEpics, ofType } from 'redux-observable' // import { ActionsObservable, combineEpics, ofType } from 'redux-observable'
import { from, of } from 'rxjs' // import { from, of } from 'rxjs'
import { catchError, switchMap } from 'rxjs/operators' // import { catchError, switchMap } from 'rxjs/operators'
import { mapSystemApiToStore } from '@agiliza/api/mappers/system' // import { mapSystemApiToStore } from '@agiliza/api/mappers/system'
import { session } from '@agiliza/api/useCases' // import { session } from '@agiliza/api/useCases'
import { actions, types } from '@agiliza/redux/ui/system' // import { actions, types } from '@agiliza/redux/ui/system'
import { getError } from '@agiliza/utils/method' // import { getError } from '@agiliza/utils/method'
type FetchMenuAction = ReturnType<typeof actions.fetchMenu> // type FetchMenuAction = ReturnType<typeof actions.fetchMenu>
const fetchMenuEpic = (action$: ActionsObservable<AnyAction>) => // const fetchMenuEpic = (action$: ActionsObservable<AnyAction>) =>
action$.pipe( // action$.pipe(
ofType<AnyAction, FetchMenuAction>(types.fetchMenu), // ofType<AnyAction, FetchMenuAction>(types.fetchMenu),
switchMap(() => // switchMap(() =>
from(session.fetchMenu()).pipe( // from(session.fetchMenu()).pipe(
switchMap((menu) => of(actions.fetchMenuSuccess(mapSystemApiToStore(menu)))), // switchMap((menu) => of(actions.fetchMenuSuccess(mapSystemApiToStore(menu)))),
catchError((error) => of(actions.fetchMenuError(getError(error)))) // catchError((error) => of(actions.fetchMenuError(getError(error))))
) // )
) // )
) // )
export default combineEpics(fetchMenuEpic) // export default combineEpics(fetchMenuEpic)
import { getActionTypes } from '@agiliza/utils/method'
import * as selectors from './selectors'
import * as shared from './shared'
import slice from './slice'
export * from './slice'
const actions = slice.actions
const reducer = slice.reducer
const types = getActionTypes(slice.actions)
export { actions, types, selectors, shared }
export default reducer
import { values } from '@agiliza/utils/method'
import { Store } from './slice'
export const getContext = (state: Store) => state
export const getContextEntities = (state: Store) => ({ states: values(state.states.byId), cities: values(state.cities.byId) })
import { normalize, schema } from 'normalizr'
import { City, GetContext, State } from '@agiliza/api/domain/authentication'
import { NormalizedEntity } from '@agiliza/utils/method'
export interface NormalizedContext {
states: NormalizedEntity<State>
cities: NormalizedEntity<City>
}
const states = new schema.Array(new schema.Entity('states'))
const cities = new schema.Array(new schema.Entity('cities'))
const contextEntities = new schema.Object({ states, cities })
export const normalizeContext = (context: GetContext['Output']) => {
const { entities } = normalize(context, contextEntities)
const normalizedEntities = entities as unknown as NormalizedContext
return normalizedEntities
}
import { City, GetContext, State } from '@agiliza/api/domain/authentication'
import { types as fetchTypes } from '@agiliza/redux/useCases/authentication'
import { EntityStore, keys } from '@agiliza/utils/method'
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { PREFIX } from '../shared'
import { normalizeContext } from './shared'
export interface Store {
states: EntityStore<State>
cities: EntityStore<City>
}
export const initialState: Store = {
states: { byId: {}, allIds: [] },
cities: { byId: {}, allIds: [] },
}
export default createSlice({
name: `${PREFIX}/authentication`,
initialState,
reducers: {},
extraReducers: {
[fetchTypes.getContext.fulfilled]: (state, action: PayloadAction<GetContext['Output']>) => {
const normalized = normalizeContext(action.payload)
console.log(normalized)
state.states.byId = normalized.states
state.states.allIds = keys(normalized.states)
state.cities.byId = normalized.cities
state.cities.allIds = keys(normalized.cities)
},
},
})
// import { initialState as profileInitState, reducer as profile, State as Profiles } from './profiles' import authentication, {
initialState as authenticationInitState,
Store as AuthenticationState
} from './authentication'
import simulation, {
initialState as simulationInitState,
State as SimulationState
} from './simulation'
import { initialState as systemInitState, reducer as system, State as System } from './system' import { initialState as systemInitState, reducer as system, State as System } from './system'
// import { initialState as userInitState, reducer as user, State as Users } from './users'
export interface EntitiesState { export interface EntitiesState {
// user: Users
// profile: Profiles
system: System system: System
simulation: SimulationState
authentication: AuthenticationState
} }
const reducers = {
// user,
// profile,
system
}
export const initialState: EntitiesState = { export const initialState: EntitiesState = {
// user: userInitState, simulation: simulationInitState,
// profile: profileInitState, system: systemInitState,
system: systemInitState authentication: authenticationInitState,
}
const reducers = {
simulation,
system,
authentication,
} }
export default reducers export default reducers
export const PREFIX = 'entities'
import { getActionTypes } from '@agiliza/utils/method'
import * as selectors from './selectors'
import slice from './slice'
export * from './slice'
const actions = slice.actions
const reducer = slice.reducer
const types = getActionTypes(slice.actions)
export { actions, types, selectors }
export default reducer
import { createSelector } from '@reduxjs/toolkit'
import { Store } from './slice'
export const getAllIds = (key: keyof Store) => (state: Store) => {
return state[key].allIds
}
export const getById = (key: keyof Store) => (state: Store) => {
return state[key].byId
}
export const getSimulationCategories = createSelector(getById('simulationCategories'), getAllIds('simulationCategories'), (byId, allIds) =>
allIds.map((id) => byId[id])
)
import { SimulationCategory } from '@agiliza/api/domain'
import { types as fetchTypes } from '@agiliza/redux/useCases/simulation'
import { EntityStore, syncAllIds, syncById } from '@agiliza/utils/method'
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { normalizeSimulationCategories, SIMULATION_PREFIX } from '../shared'
export interface Store {
simulationCategories: EntityStore<SimulationCategory>
}
export const initialState: Store = {
simulationCategories: { byId: {}, allIds: [] },
}
export default createSlice({
name: `${SIMULATION_PREFIX}/context`,
initialState,
reducers: {},
extraReducers: {
[fetchTypes.fetchSimulationCategories.fulfilled]: (state, action: PayloadAction<SimulationCategory[]>) => {
const normalized = normalizeSimulationCategories(action.payload)
state.simulationCategories.byId = syncById(state.simulationCategories, normalized.simulationCategories)
state.simulationCategories.allIds = syncAllIds(state.simulationCategories, normalized.simulationCategories)
},
},
})
import { combineReducers } from 'redux'
import context, { initialState as contextInitState, Store as ContextStore } from './context'
import simulation, {
initialState as simulationInitState,
Store as SimulationStore
} from './simulation'
export interface State {
context: ContextStore
simulation: SimulationStore
}
export const initialState: State = {
context: contextInitState,
simulation: simulationInitState,
}
export default combineReducers({
context,
simulation,
})
import { normalize, schema } from 'normalizr'
import { SimulationCategory, SubProduct } from '@agiliza/api/domain'
import { NormalizedEntity } from '@agiliza/utils/method'
export const SIMULATION_PREFIX = 'entities/simulation'
export interface NormalizedSimulationCategories {
simulationCategories: NormalizedEntity<SimulationCategory>
}
export interface NormalizedSubproducts {
subproducts: NormalizedEntity<SubProduct>
}
const simulationCategory = new schema.Array(new schema.Entity('simulationCategories'))
const subProduct = new schema.Array(new schema.Entity('subproducts'))
export const normalizeSimulationCategories = (sCs: SimulationCategory[]) => {
const { entities } = normalize(sCs, simulationCategory)
const normalizedEntities = entities as unknown as NormalizedSimulationCategories
return normalizedEntities
}
export const normalizeSubproducts = (sPs: SubProduct[]) => {
const { entities } = normalize(sPs, subProduct)
const normalizedEntities = entities as unknown as NormalizedSubproducts
return normalizedEntities
}
import { getActionTypes } from '@agiliza/utils/method'
import * as selectors from './selectors'
import slice from './slice'
export * from './slice'
const actions = slice.actions
const reducer = slice.reducer
const types = getActionTypes(slice.actions)
export { actions, types, selectors }
export default reducer
import { createSelector } from '@reduxjs/toolkit'
import { Store } from './slice'
export const getAllIds = (key: keyof Store) => (state: Store) => {
return state[key].allIds
}
export const getById = (key: keyof Store) => (state: Store) => {
return state[key].byId
}
export const getSubproducts = createSelector(getById('subproducts'), getAllIds('subproducts'), (byId, allIds) => allIds.map((id) => byId[id]))
import { GetSubProducts, SubProduct } from '@agiliza/api/domain'
import { types as fetchTypes } from '@agiliza/redux/useCases/simulation'
import { EntityStore, syncAllIds, syncById } from '@agiliza/utils/method'
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { normalizeSubproducts, SIMULATION_PREFIX } from '../shared'
export interface Store {
subproducts: EntityStore<SubProduct>
}
export const initialState: Store = {
subproducts: { byId: {}, allIds: [] },
}
export default createSlice({
name: `${SIMULATION_PREFIX}/simulation`,
initialState,
reducers: {},
extraReducers: {
[fetchTypes.getSubproducts.fulfilled]: (state, action: PayloadAction<GetSubProducts['Output']>) => {
const normalized = normalizeSubproducts(action.payload)
state.subproducts.byId = syncById(state.subproducts, normalized.subproducts)
state.subproducts.allIds = syncAllIds(state.subproducts, normalized.subproducts)
},
},
})
import deepmerge from 'deepmerge' import deepmerge from 'deepmerge'
import { AnyAction, combineReducers, Middleware, Reducer } from 'redux' import { AnyAction, combineReducers, Middleware, Reducer } from 'redux'
import { createLogger } from 'redux-logger'
import { combineEpics, createEpicMiddleware, Epic } from 'redux-observable' import { combineEpics, createEpicMiddleware, Epic } from 'redux-observable'
import { sessionEpic } from '@agiliza/epics' // import { sessionEpic } from '@agiliza/epics'
import { configureStore } from '@reduxjs/toolkit' import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit'
import entitiesReducer, { EntitiesState, initialState as entitiesInitState } from './entities' import entitiesReducer, { EntitiesState, initialState as entitiesInitState } from './entities'
import { errorMiddleware } from './middlewares' import { errorMiddleware, printMiddleware } from './middlewares'
import { import {
initialState as sessionInitState, initialState as sessionInitState,
reducer as sessionReducer, reducer as sessionReducer,
State as SessionState State as SessionState
} from './session' } from './session'
import uiReducer, { initialState as uiInitState, UIState } from './ui' import uiReducer, { initialState as uiInitState, UIState } from './ui'
import useCases, { initialState as useCasesInitState, UseCasesState } from './useCases'
type ReducerMap = Record<string, Reducer> type ReducerMap = Record<string, Reducer>
type Reducers = Record<string, Reducer | ReducerMap> type Reducers = Record<string, Reducer | ReducerMap>
...@@ -22,6 +22,7 @@ let registeredReducers: Reducers = { ...@@ -22,6 +22,7 @@ let registeredReducers: Reducers = {
entities: entitiesReducer, entities: entitiesReducer,
session: sessionReducer, session: sessionReducer,
ui: uiReducer, ui: uiReducer,
useCases,
} }
function recombineReducers(reducers: Reducers) { function recombineReducers(reducers: Reducers) {
...@@ -42,17 +43,23 @@ const rootReducer = (state, action: AnyAction) => { ...@@ -42,17 +43,23 @@ const rootReducer = (state, action: AnyAction) => {
return combinedReducers(state, action) return combinedReducers(state, action)
} }
const middlewares = !process.env.PRODUCTION ? [createLogger({ collapsed: true }), errorMiddleware] : [errorMiddleware]
const defaultMiddleware = getDefaultMiddleware({
serializableCheck: false,
})
const middlewares = !process.env.PRODUCTION ? [/*createLogger({ collapsed: true }), */ errorMiddleware, printMiddleware] : [errorMiddleware]
const epicMiddleware = createEpicMiddleware() const epicMiddleware = createEpicMiddleware()
const store = configureStore({ const store = configureStore({
reducer: rootReducer, reducer: rootReducer,
middleware: [...middlewares, epicMiddleware] as Middleware[], middleware: [...defaultMiddleware, ...middlewares, epicMiddleware] as Middleware[],
preloadedState: { preloadedState: {
session: sessionInitState, session: sessionInitState,
ui: uiInitState, ui: uiInitState,
entities: entitiesInitState, entities: entitiesInitState,
useCases: useCasesInitState,
}, },
devTools: process.env.NODE_ENV === 'development', devTools: process.env.NODE_ENV === 'development',
}) })
...@@ -61,7 +68,7 @@ type EpicMap = Record<string, Epic> ...@@ -61,7 +68,7 @@ type EpicMap = Record<string, Epic>
type Epics = Record<string, Epic | EpicMap> type Epics = Record<string, Epic | EpicMap>
let registeredEpics: Epics = { let registeredEpics: Epics = {
session: sessionEpic, // session: sessionEpic,
} }
function recombineEpics(epics: Epics) { function recombineEpics(epics: Epics) {
...@@ -98,6 +105,7 @@ export interface StoreState { ...@@ -98,6 +105,7 @@ export interface StoreState {
ui: UIState ui: UIState
session: SessionState session: SessionState
entities: EntitiesState entities: EntitiesState
useCases: UseCasesState
} }
const wd: any = window const wd: any = window
......
import { getError } from '@agiliza/utils/method' import { ApiError } from '@agiliza/api/domain'
import { Dispatch, Middleware, PayloadAction } from '@reduxjs/toolkit' import { ErrorTypes } from '@agiliza/constants/error'
import * as ucLogout from '@agiliza/redux/ui/login'
import { Middleware, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit'
import { actions as errorActions } from './ui/error' import { actions as errorActions } from './ui/error'
export const errorMiddleware: Middleware = // export const errorMiddleware: Middleware =
({ dispatch }) => // ({ dispatch }) =>
(next: Dispatch<PayloadAction<any>>) => // (next: Dispatch<PayloadAction<any>>) =>
((action: PayloadAction<any>) => { // ((action: PayloadAction<any>) => {
if (/Error$/.test(action.type) && action.payload) { // if (/Error$/.test(action.type) && action.payload) {
const error = getError(action.payload) // const error = getError(action.payload)
dispatch(errorActions.setErrorMessage(error)) // dispatch(errorActions.setErrorMessage(error))
// }
// return next(action)
// }) as Dispatch<PayloadAction>
export const errorMiddleware: Middleware = (api) => (next: ThunkDispatch<any, any, any>) => (action: PayloadAction<ApiError>) => {
if (action.type.includes('/rejected')) {
const error = action.payload
// console.log(action);
// console.log(error)
// Todo erro de sessão expirada deve imediatamente remover a autenticação do usuário.
if (error.type === ErrorTypes.EXPIRED_SESSION) {
api.dispatch(ucLogout.actions.logout() as any)
} else if (!action.type.includes('/connect') && (action.type.includes('useCases') || action.type.includes('ui'))) {
if (error.type !== ErrorTypes.FORM_VALIDATION && error.type !== ErrorTypes.MISSING_INPUT) {
api.dispatch(errorActions.setErrorMessage(error.message || 'Erro de autenticação'))
} }
return next(action) }
}) as Dispatch<PayloadAction> }
return next(action)
}
export const printMiddleware: Middleware =
(api) => (next: ThunkDispatch<any, any, any>) => (action: PayloadAction<any, string, { arg: Record<string, unknown> | string }>) => {
if (action.type.includes('/pending')) {
console.log(
`%c ${action.type.substring(0, action.type.lastIndexOf('/'))} %c`,
`color: white; font-weight: bolder; background: blue`,
'font-weight: bolder; font-style: italic;',
action.meta.arg || ''
)
}
if (action.type.includes('/fulfilled')) {
console.log(
`%c ${action.type.substring(0, action.type.lastIndexOf('/'))} %c`,
`color: white; font-weight: bolder; background: green`,
'font-weight: bolder; font-style: italic;',
action.payload ? (typeof action.payload === 'object' ? (Object.keys(action.payload).length !== 0 ? action.payload : '') : action.payload) : ''
)
}
return next(action)
}
import { getTypesActions } from '@agiliza/utils/method' import { getTypesActions } from '@agiliza/utils/method'
import session from './reducer' import session from './reducer'
import * as selectors from './selectors'
export * from './reducer' export * from './reducer'
...@@ -9,3 +10,5 @@ export const reducer = session.reducer ...@@ -9,3 +10,5 @@ export const reducer = session.reducer
export const types = getTypesActions(session.actions) export const types = getTypesActions(session.actions)
export default session export default session
export { selectors }
import { ConfigService } from '@agiliza/curio/SessionManager' import { Customer } from '@agiliza/api/domain'
import { types as loginTypes } from '@agiliza/redux/ui/login'
import { types as authenticationTypes } from '@agiliza/redux/useCases/authentication'
import { createSlice, PayloadAction } from '@reduxjs/toolkit' import { createSlice, PayloadAction } from '@reduxjs/toolkit'
export interface State { export interface State {
...@@ -6,7 +8,7 @@ export interface State { ...@@ -6,7 +8,7 @@ export interface State {
authenticating: boolean authenticating: boolean
authenticated: boolean authenticated: boolean
error?: string error?: string
serviceInfo?: ConfigService customer?: Customer
} }
export const initialState: State = { export const initialState: State = {
...@@ -15,42 +17,27 @@ export const initialState: State = { ...@@ -15,42 +17,27 @@ export const initialState: State = {
initializing: false, initializing: false,
} }
export type Actions = 'setServiceInfo' | 'authRequest' | 'authSuccess' | 'authError' | 'logout' | 'logoutSuccess'
const session = createSlice({ const session = createSlice({
name: 'session', name: 'session',
initialState, initialState,
reducers: { reducers: {},
initialize: (state) => { extraReducers: {
state.initializing = true [loginTypes.login.fulfilled]: (state, action: PayloadAction<Customer>) => {
},
initializeSuccess: (state) => {
state.initializing = false
},
initializeError: (state, action: PayloadAction<string | undefined>) => {
state.initializing = false
state.error = action.payload
},
authRequest: (state) => {
state.authenticating = true
state.error = undefined
},
authSuccess: (state) => {
state.authenticating = false
state.authenticated = true state.authenticated = true
state.customer = action.payload
console.log(action.payload)
}, },
authError: (state, action: PayloadAction<string | undefined>) => { [loginTypes.logout.fulfilled]: (state) => {
state.authenticating = false
state.error = action.payload
},
logout: (state) => {
state.error = undefined
},
logoutSuccess: (state) => {
state.authenticated = false state.authenticated = false
}, },
setServiceInfo: (state, action: PayloadAction<ConfigService>) => { [loginTypes.connect.fulfilled]: (state) => {
state.serviceInfo = action.payload state.authenticated = true
},
// [authenticationTypes.createCustomer.fulfilled]: (state, action: PayloadAction<Customer>) => {
// state.customer = action.payload
// },
[authenticationTypes.getLoggedCustomer.fulfilled]: (state, action: PayloadAction<Customer>) => {
state.customer = action.payload
}, },
}, },
}) })
......
...@@ -4,4 +4,4 @@ export const isAuthenticating = (state: State) => state.authenticating ...@@ -4,4 +4,4 @@ export const isAuthenticating = (state: State) => state.authenticating
export const isAuthenticated = (state: State) => state.authenticated export const isAuthenticated = (state: State) => state.authenticated
export const isInitializing = (state: State) => state.initializing export const isInitializing = (state: State) => state.initializing
export const getError = (state: State) => state.error export const getError = (state: State) => state.error
export const getServiceInfo = (state: State) => state.serviceInfo export const getCustomer = (state: State) => state.customer
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
State as DrawerState State as DrawerState
} from './drawer' } from './drawer'
import { initialState as errorInitialState, reducer as error, State as ErrorState } from './error' import { initialState as errorInitialState, reducer as error, State as ErrorState } from './error'
import { initialState as loginInitialState, reducer as login, State as LoginState } from './login' import login, { initialState as loginInitialState, State as LoginState } from './login'
// import { // import {
// initialState as profileInitialState, // initialState as profileInitialState,
// reducer as profile, // reducer as profile,
...@@ -35,7 +35,7 @@ const reducers = { ...@@ -35,7 +35,7 @@ const reducers = {
drawer, drawer,
// user, // user,
// profile, // profile,
system system,
} }
export const initialState: UIState = { export const initialState: UIState = {
...@@ -44,7 +44,7 @@ export const initialState: UIState = { ...@@ -44,7 +44,7 @@ export const initialState: UIState = {
drawer: drawerInitialState, drawer: drawerInitialState,
// user: userInitialState, // user: userInitialState,
// profile: profileInitialState, // profile: profileInitialState,
system: systemInitialState system: systemInitialState,
} }
export default reducers export default reducers
import { getTypesActions } from '@agiliza/utils/method'
import login from './reducer'
import * as selectors from './selectors' import * as selectors from './selectors'
import slice from './slice'
export * from './reducer' export * from './slice'
const actions = login.actions export { selectors }
const reducer = login.reducer export default slice.reducer
const types = getTypesActions(login.actions)
export { actions, reducer, types, selectors }
export default login
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
export interface State {
fetching: boolean
}
export const initialState: State = {
fetching: false,
}
interface Login {
username: string
password: string
}
const login = createSlice({
name: 'ui/login',
initialState,
reducers: {
login(state, action: PayloadAction<Login>) {
state.fetching = true
},
loginSuccess(state) {
state.fetching = false
},
loginError(state, action: PayloadAction<string>) {
state.fetching = false
},
},
})
export default login
import { State } from './reducer' import { State } from './slice'
export const isFetching = (state: State) => state.fetching export const isFetching = (state: State) => state.fetching
import { SessionRepositoryImplFactory } from '@agiliza/api/useCases/session'
import { appPlatform } from '@agiliza/constants/platform'
import {
createAsyncReducers,
getTypesThunkActions,
values,
WithSuccess
} from '@agiliza/utils/method'
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit'
const prefix = 'ui/login'
export interface State {
fetching: boolean
}
export const initialState: State = {
fetching: false,
}
interface Login {
username: string
password: string
}
export const actions = {
login: createAsyncThunk(`${prefix}/login`, async (args: WithSuccess<{ username: string; password: string }>, thunkApi) => {
const useCase = SessionRepositoryImplFactory.create(appPlatform)
try {
const { username, password, onSuccess } = args
const customer = await useCase.login(username, password)
onSuccess && onSuccess()
return customer
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
logout: createAsyncThunk(`${prefix}/logout`, (_, thunkApi) => {
const useCase = SessionRepositoryImplFactory.create(appPlatform)
useCase.logout()
thunkApi.dispatch({ type: 'RESET_STORE' })
return
}),
connect: createAsyncThunk(`${prefix}/connect`, (_, thunkApi) => {
const useCase = SessionRepositoryImplFactory.create(appPlatform)
try {
useCase.connect()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
} as const
export const types = getTypesThunkActions(actions)
const login = createSlice({
name: prefix,
initialState,
reducers: {
login(state, action: PayloadAction<Login>) {
state.fetching = true
},
loginSuccess(state) {
state.fetching = false
},
loginError(state, action: PayloadAction<string>) {
state.fetching = false
},
},
extraReducers: {
...values(types).reduce((reducers, type) => ({ ...reducers, ...createAsyncReducers(type) }), {}),
},
})
export default login
import * as selectors from './selectors'
import slice from './slice'
export * from './slice'
export { selectors }
export default slice.reducer
import { State } from './slice'
export const isFetching = (state: State) => state.fetching
export const isSendingCode = (state: State) => state.sendingCode
import {
CreateCustomer,
CreatePassword,
GetLoggedCustomer,
SendCode,
VerifyCode,
VerifyCPF
} from '@agiliza/api/domain'
import { AuthenticationRepositoryImplFactory } from '@agiliza/api/useCases'
import { appPlatform } from '@agiliza/constants/platform'
import {
createAsyncReducers,
getTypesThunkActions,
values,
WithSuccess
} from '@agiliza/utils/method'
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
const prefix = 'useCases/authentication'
export interface State {
fetching: boolean
sendingCode: boolean
}
export const initialState: State = {
fetching: false,
sendingCode: false,
}
export const actions = {
getContext: createAsyncThunk(`${prefix}/getContext`, async (_, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
return await useCase.getContext()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
verifyCPF: createAsyncThunk(`${prefix}/verifyCPF`, async (input: WithSuccess<VerifyCPF['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
await useCase.verifyCPF(input)
input.onSuccess && input.onSuccess()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
createCustomer: createAsyncThunk(`${prefix}/createCustomer`, async (input: WithSuccess<CreateCustomer['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
const customer = await useCase.createCustomer(input)
input.onSuccess && input.onSuccess()
return customer
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
sendCode: createAsyncThunk(`${prefix}/sendCode`, async (input: WithSuccess<SendCode['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
await useCase.sendCode(input)
input.onSuccess && input.onSuccess()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
verifyCode: createAsyncThunk(`${prefix}/verifyCode`, async (input: WithSuccess<VerifyCode['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
await useCase.verifyCode(input)
input.onSuccess && input.onSuccess()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
createPassword: createAsyncThunk(`${prefix}/createPassword`, async (input: WithSuccess<CreatePassword['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
await useCase.createPassword(input)
input.onSuccess && input.onSuccess()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
getLoggedCustomer: createAsyncThunk(`${prefix}/getLoggedCustomer`, async (input: WithSuccess<GetLoggedCustomer['Input']>, thunkApi) => {
const useCase = AuthenticationRepositoryImplFactory.create(appPlatform)
try {
const customer = await useCase.getLoggedCustomer()
input.onSuccess && input.onSuccess()
return customer
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
} as const
export const types = getTypesThunkActions(actions)
const slice = createSlice({
name: prefix,
initialState,
reducers: {},
extraReducers: {
...values(types).reduce((reducers, type) => ({ ...reducers, ...createAsyncReducers(type) }), {}),
...createAsyncReducers(types.sendCode, 'sendingCode'),
},
})
export default slice
import authentication, {
initialState as authenticationInitialState,
State as AuthenticationState
} from './authentication'
import simulation, {
initialState as simulationInitialState,
State as SimulationState
} from './simulation'
export interface UseCasesState {
simulation: SimulationState
authentication: AuthenticationState
}
const reducers = {
simulation,
authentication,
}
export const initialState: UseCasesState = {
simulation: simulationInitialState,
authentication: authenticationInitialState,
}
export default reducers
import * as selectors from './selectors'
import slice from './slice'
export * from './slice'
export { selectors }
export default slice.reducer
import { State } from './slice'
export const isFetching = (state: State) => state.fetching
import { GetSubProducts } from '@agiliza/api/domain'
import { SimulationContextRepositoryImplFactory } from '@agiliza/api/useCases'
import { SimulationRepositoryImplFactory } from '@agiliza/api/useCases/simulation/simulation'
import { appPlatform } from '@agiliza/constants/platform'
import {
createAsyncReducers,
getTypesThunkActions,
values,
WithSuccess
} from '@agiliza/utils/method'
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
const prefix = 'useCases/simulation'
export interface State {
fetching: boolean
}
export const initialState: State = {
fetching: false,
}
export const actions = {
fetchSimulationCategories: createAsyncThunk(`${prefix}/fetchSimulationCategories`, async (_, thunkApi) => {
const useCase = SimulationContextRepositoryImplFactory.create(appPlatform)
try {
return await useCase.fetchSimulationCategories()
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}),
getSubproducts: createAsyncThunk(
`${prefix}/getSubproducts`,
async (input: WithSuccess<GetSubProducts['Input'], GetSubProducts['Output']>, thunkApi) => {
const useCase = SimulationRepositoryImplFactory.create(appPlatform)
try {
const response = await useCase.getSubproducts(input)
input.onSuccess && input.onSuccess(response)
return response
} catch (e) {
return thunkApi.rejectWithValue(e)
}
}
),
} as const
export const types = getTypesThunkActions(actions)
const login = createSlice({
name: prefix,
initialState,
reducers: {},
extraReducers: {
...values(types).reduce((reducers, type) => ({ ...reducers, ...createAsyncReducers(type) }), {}),
},
})
export default login
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment