Compare commits

13 Commits

Author SHA1 Message Date
0f764b1239 docker compose 2026-01-21 22:05:21 +00:00
0df0baf822 Fix Geradores 2025-11-03 21:28:23 +00:00
32315e179e Push 2025-10-07 21:02:53 +01:00
bd8d5677b4 jenkins user 2025-10-06 23:22:52 +01:00
381b750841 fix user 2025-10-06 23:19:35 +01:00
2b413e479a fix 2025-10-06 23:17:05 +01:00
a6707fe731 Jenkins Git cred 2025-10-06 23:15:58 +01:00
19ad8deb6f Paginas 2025-10-06 23:08:28 +01:00
Marco Santos
6e697e40c1 Fix jenkins 2025-07-24 11:55:16 +01:00
Marco Santos
b25a39df8c Fix a generate 2025-07-24 11:40:08 +01:00
Marco Santos
be3085ce15 master 2025-07-24 02:36:31 +01:00
Marco Santos
ed729b9c8e jenkinsfile 2025-07-24 02:12:56 +01:00
e06815053a Merge pull request 'Novo Layout' (#1) from newlayout into master
Reviewed-on: Marco/Geradores#1
2025-07-24 00:08:59 +01:00
22 changed files with 464 additions and 365 deletions

156
Jenkinsfile vendored
View File

@@ -1,171 +1,103 @@
pipeline {
agent any
parameters {
booleanParam(name: 'DeployAll', defaultValue: false, description: 'Deploy Site/WS ?')
booleanParam(name: 'deploySite', defaultValue: false, description: 'Deploy Site ?')
}
triggers {
pollSCM('* * * * *')
}
/*
environment {
now = new Date().format('yyyyMMdd-HHmm', TimeZone.getTimeZone('UTC'))
DOCKER_HOST = 'tcp://192.168.2.20:2375'
DOCKER_REGISTRY = 'git.homeware.pt/marco'
DOCKER_TAG = "${env.BUILD_ID}"
DOCKER_REGISTRY = 'Shini89' //'your-docker-registry.com' // Registro Docker
REACT_DIR = 'geradoresfe'
REACT_DOCKER_IMAGE = 'shini89/geradoresfe:lastest'
SERVICE_DIR = 'GeradoresService'
API_DIR = 'GeradoresWS'
API_DOCKER_IMAGE = 'shini89/geradoresws:lastest'
}
*/
environment {
DOCKER_HOST = 'tcp://192.168.2.20:2375'
DOCKER_REGISTRY = 'docker.io/shini89'
DOCKER_TAG = "${env.BUILD_ID}" // Tag única para builds
REACT_DIR = 'geradoresfe'
SERVICE_DIR = 'GeradoresService'
API_DIR = 'GeradoresWS'
REACT_IMAGE = "${DOCKER_REGISTRY}/geradores/fe:${DOCKER_TAG}"
WS_IMAGE = "${DOCKER_REGISTRY}/geradores/ws:${DOCKER_TAG}"
DOCKER_CREDENTIALS_ID = 'docker-registry-creds'
KUBECONFIG_CREDENTIALS_ID = 'kubeconfig-jenkins'
GIT_CREDENTIALS_ID = '3e806a5c-ee46-49fb-974d-15f3fe2e1d49'
}
stages {
stage('Validate Docker Connection') {
stage('Preparar Ambiente') {
steps {
script {
echo "Validando conexão com Docker remoto: ${DOCKER_HOST}"
// Testa a conexão com o Docker remoto
sh 'docker --version'
echo "Verificar Docker local"
sh 'docker version'
}
}
}
stage('Check for Changes') {
stage('Checkout Repositório') {
steps {
script {
def message = ""
CodeChanges = currentBuild.changeSets != []
if (CodeChanges) {
def changeLogSets = currentBuild.changeSets
for (int i = 0; i < changeLogSets.size(); i++) {
def entries = changeLogSets[i].items
for (int j = 0; j < entries.length; j++) {
def entry = entries[j]
message = message + "<br>Autor: ${entry.author.fullName} ( ${entry.author} ) <br> Commit: ${entry.msg}"
def files = new ArrayList(entry.affectedFiles)
message = message + '<br>Ficheiros:<br><ul>'
for (int k = 0; k < files.size(); k++) {
def file = files[k]
message = message + "<li> ${file.path} ( ${file.editType.name} ) </li>"
}
message = message + '</ul>'
}
}
}
}
git branch: 'master',
url: 'https://git.homeware.pt/Marco/Geradores.git',
credentialsId: env.GIT_CREDENTIALS_ID
}
}
stage('Checkout Code') {
stage('Build .NET Services') {
steps {
git branch: 'master', url: 'https://git.homeware.pt/Marco/Geradores.git'
dir("${SERVICE_DIR}") {
sh 'dotnet build -c Release'
}
}
/*******************************************************
Stage BUILD
*******************************************************/
stage('Build DLL') {
steps {
dir("${env.SERVICE_DIR}") {
dir("${API_DIR}") {
sh 'dotnet build -c Release'
}
}
}
stage('Build WS') {
stage('Build React App') {
steps {
dir("${env.API_DIR}") {
sh 'dotnet build -c Release'
}
}
}
stage('Build WS Docker Image') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/geradoresws:${DOCKER_TAG}", "-f ${API_DIR}/Dockerfile .")
}
}
}
stage('Build React') {
steps {
dir("${env.REACT_DIR}") {
dir("${REACT_DIR}") {
sh 'npm install'
sh 'npm run build'
/*
sh """
docker build -t ${env.DOCKER_REGISTRY}/react-frontend:latest .
docker push ${env.DOCKER_REGISTRY}/react-frontend:latest
"""*/
}
}
}
stage('Build React Docker Image') {
steps {
dir("${env.REACT_DIR}") {
script {
docker.build("${DOCKER_REGISTRY}/geradoresfe:${DOCKER_TAG}", ".")
}
}
}
}
stage('Push Docker Images') {
stage('Docker Build Imagens') {
steps {
script {
docker.withRegistry('https://index.docker.io/v1/', 'docker-credentials') {
docker.image("${DOCKER_REGISTRY}/geradoresws:${DOCKER_TAG}").push()
docker.image("${DOCKER_REGISTRY}/geradoresfe:${DOCKER_TAG}").push()
}
docker.build("${WS_IMAGE}")
docker.build("${REACT_IMAGE}", "${REACT_DIR}")
}
}
}
stage('Deploy Containers Locally') {
stage('Push para Docker Registry') {
steps {
script {
// Remove contêiner antigo (se existir)
sh """
docker rm -f geradoresws-container || true
docker rm -f geradoresfe-container || true
"""
docker.withRegistry("https://${DOCKER_REGISTRY}", GIT_CREDENTIALS_ID) {
docker.image("${WS_IMAGE}").push()
docker.image("${REACT_IMAGE}").push()
}
}
}
}
// Executa contêineres com as novas imagens
stage('Deploy no Kubernetes') {
steps {
withKubeConfig(credentialsId: KUBECONFIG_CREDENTIALS_ID) {
script {
// Substitui as imagens no manifest e aplica
sh """
docker run -d --name geradoresws-container -p 32772:8080 ${DOCKER_REGISTRY}/geradoresws:${DOCKER_TAG}
docker run -d --name geradoresfe-container -p 3000:3000 ${DOCKER_REGISTRY}/geradoresfe:${DOCKER_TAG}
sed 's|{{WS_IMAGE}}|${WS_IMAGE}|g; s|{{FE_IMAGE}}|${REACT_IMAGE}|g' k8s/deployment.template.yaml > k8s/deployment.yaml
kubectl apply -f k8s/deployment.yaml
"""
}
}
}
}
}
post {
success {
echo 'Services deployed successfully!'
echo 'Deploy feito com sucesso!'
sh 'docker system prune -f'
}
failure {
echo 'Failed to deploy services. Check logs.'
echo 'Erro no pipeline. Verifica os logs!'
}
}
}

View File

@@ -2,3 +2,6 @@
<code> docker build -t shini89/geradoresws ..</code>
<code> docker run --rm -p 44329:8080 shini89/geradoresws</code>
<code>npx swagger-typescript-api generate -p http://localhost:29191/swagger/v1/swagger.json -o src/service -n api.ts --enum-names-as-values</code>
npx swagger-typescript-api generate -p http://localhost:29191/swagger/v1/swagger.json -o src/service -n api.ts --http-client axios --enum-names-as-values

View File

@@ -18,11 +18,11 @@ services:
dockerfile: Dockerfile
image: docker.io/shini89/geradoresfe:latest
ports:
- "3000:3000"
- "3000:80"
depends_on:
- geradoresws
environment:
- REACT_APP_API_URL=http://localhost:5050/
- VITE_API_URL=http://localhost:5050/
networks:
- app-network

View File

@@ -1 +1 @@
VITE_API_URL=http://localhost:44329/
VITE_API_URL=https://localhost:7266/

View File

@@ -1,8 +1,26 @@
FROM node:20-alpine
# ---------- BUILD ----------
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json .
COPY package.json package-lock.json ./
RUN npm install
COPY . .
ENV REACT_APP_API_URL=http://localhost:5015/
EXPOSE 3000
CMD ["npm", "start"]
# Variável de ambiente correta para Vite
ENV VITE_API_URL=http://localhost:5050/
RUN npm run build
# ---------- SERVE ----------
FROM nginx:alpine
# Copia os ficheiros do build para a pasta do nginx
COPY --from=build /app/dist /usr/share/nginx/html
# Configuração mínima do nginx (opcional: para SPA fallback)
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

14
geradoresfe/nginx.conf Normal file
View File

@@ -0,0 +1,14 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri /index.html;
}
# Optional: gzip
gzip on;
gzip_types text/plain application/javascript text/css application/json image/svg+xml;
}

View File

@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"build": "vite build",
"lint": "eslint .",
"preview": "vite preview"
},
@@ -35,6 +35,7 @@
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
"postcss": "^8.5.6",
"swagger-typescript-api": "^13.2.7",
"tailwindcss": "^4.1.11",
"typescript": "~5.8.3",
"typescript-eslint": "^8.35.1",

View File

@@ -1,6 +1,5 @@
import axios from 'axios';
import type { AxiosResponse } from 'axios';
import { NIFType } from '../service/api';
import { toast } from 'react-hot-toast';
const API_URL = import.meta.env.VITE_API_URL;
@@ -8,6 +7,7 @@ const API_URL = import.meta.env.VITE_API_URL;
class GeradorService {
static async GenerateNIF(type: string | null): Promise<any[]> {
try {
debugger;
const response: AxiosResponse = await axios.get(API_URL + 'Generate/GenerateNIF',
{
params: {
@@ -15,7 +15,7 @@ class GeradorService {
}
});
return response.data;
} catch (error) {
} catch (error : any) {
toast.error('Error fetching NIF:' + error.message);
return [];

View File

@@ -3,6 +3,9 @@ import { Toaster } from 'react-hot-toast';
import Home from './pages/Home';
import { Layout } from './layout/Layout';
import { useTheme } from './context/ThemeContext';
import NIF from './pages/NIF';
import CC from './pages/CC';
import NISS from './pages/NISS';
function App() {
const { theme } = useTheme();
@@ -12,9 +15,9 @@ function App() {
<Layout>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/NIF" />
<Route path="/NISS" />
<Route path="/CC" />
<Route path="/NIF" element={<NIF />} />
<Route path="/NISS" element={<NISS />} />
<Route path="/CC" element={<CC />} />
</Routes>
<Toaster position="top-right"
toastOptions={{
@@ -22,8 +25,7 @@ function App() {
background: theme === 'dark' ? '#1f2937' : '#fff',
color: theme === 'dark' ? '#fff' : '#000',
},
}}
theme={theme} />
}} />
</Layout>
</Router>
);

View File

@@ -1,16 +1,17 @@
import React, { useState } from "react";
// src/components/Geradores/GeradorCard.tsx
import { useCallback, useState } from "react";
import { RefreshCwIcon, CopyIcon } from "lucide-react";
import { Button } from "../ui/Button";
import { Input } from "../ui/Input";
import { Select } from "../ui/Select";
import { ResultDisplay } from "../ui/ResultDisplay";
import { toast } from 'react-hot-toast';
import { toast } from "react-hot-toast";
type Parametro =
| { nome: string; tipo: "text" | "number" | "password"; placeholder?: string }
| { nome: string; tipo: "select"; options: { label: string; value: string }[] };
export type Parametro =
| { nome: string; tipo: "text" | "number" | "password"; placeholder?: string; default?: string }
| { nome: string; tipo: "select"; options: { label: string; value: string }[]; default?: string };
type GeradorCardProps = {
export type GeradorCardProps = {
readonly titulo: string;
readonly descricao?: string;
readonly parametros?: Parametro[];
@@ -25,50 +26,92 @@ export default function GeradorCard({
onGerar,
onValidar,
}: GeradorCardProps) {
const [resultado, setResultado] = useState("");
const [paramInputs, setParamInputs] = useState<Record<string, string>>({});
const [validado, setValidado] = useState<boolean | null>(null);
const handleInputChange = (nome: string, valor: string) => {
setParamInputs((prev) => ({ ...prev, [nome]: valor }));
};
const handleGerar = async () => {
const gerado = await onGerar(paramInputs);
setResultado(gerado);
setValidado(null);
};
const handleValidar = async () => {
if (onValidar) {
const isValid = await onValidar(resultado);
setValidado(isValid);
// Inicializa paramInputs com defaults vindos de `parametros`
const initialParamInputs = parametros.reduce<Record<string, string>>((acc, p) => {
debugger;
if (p.default) {
acc[p.nome] = p.default;
} else {
acc[p.nome] = "";
}
};
return acc;
}, {});
const handleCopiar = () => {
const [resultado, setResultado] = useState("");
const [paramInputs, setParamInputs] = useState<Record<string, string>>(initialParamInputs);
const [validado, setValidado] = useState<boolean | null>(null);
const [loading, setLoading] = useState(false);
const handleInputChange = useCallback((nome: string, valor: string) => {
setParamInputs((prev) => ({ ...prev, [nome]: valor }));
}, []);
const handleGerar = useCallback(async () => {
setLoading(true);
setValidado(null);
try {
const gerado = await onGerar(paramInputs);
setResultado(typeof gerado === "string" ? gerado : String(gerado ?? ""));
toast.success(`${titulo} gerado com sucesso.`, { duration: 2000 });
} catch (err) {
console.error("Erro ao gerar:", err);
toast.error("Falha ao gerar. Tente novamente.");
} finally {
setLoading(false);
}
}, [onGerar, paramInputs, titulo]);
const handleValidar = useCallback(async () => {
if (!onValidar) return;
if (!resultado) {
toast.error("Nenhum valor para validar.");
return;
}
setLoading(true);
try {
const isValid = await onValidar(resultado);
setValidado(Boolean(isValid));
if (isValid) {
toast.success("Válido", { duration: 1500 });
} else {
toast.error("Inválido", { duration: 1500 });
}
} catch (err) {
console.error("Erro ao validar:", err);
toast.error("Falha na validação. Tente novamente.");
setValidado(null);
} finally {
setLoading(false);
}
}, [onValidar, resultado]);
const handleCopiar = useCallback(async () => {
if (!resultado) return;
navigator.clipboard.writeText(resultado);
toast.success(`${titulo} copiado!`,
{
duration: 3000
});
};
try {
await navigator.clipboard.writeText(resultado);
toast.success(`${titulo} copiado!`, { duration: 2000 });
} catch (err) {
console.error("Erro ao copiar:", err);
toast.error("Não foi possível copiar para a área de transferência.");
}
}, [resultado, titulo]);
const handleResultadoChange = (valor: string) => {
const handleResultadoChange = useCallback((valor: string) => {
setResultado(valor);
setValidado(null); // resetar estado de validação ao editar
};
setValidado(null); // reset ao editar
}, []);
return (
<div className="w-full max-w-md rounded-2xl border border-gray-200 bg-white dark:bg-zinc-900 p-6 shadow-lg hover:shadow-xl transition-shadow duration-300">
{/* Título e descrição */}
<div
className="w-full max-w-md rounded-2xl border border-gray-200 bg-white dark:bg-zinc-900 p-6 shadow-lg hover:shadow-xl transition-shadow duration-300"
aria-busy={loading}
>
<div className="mb-6">
<h3 className="text-xl font-bold text-blue-700 dark:text-blue-400">{titulo}</h3>
{descricao && <p className="text-sm text-gray-600 dark:text-gray-400 mt-1">{descricao}</p>}
</div>
{/* Parâmetros dinâmicos */}
{parametros.length > 0 && (
<div className="space-y-4 mb-6">
{parametros.map((param) => {
@@ -78,9 +121,10 @@ export default function GeradorCard({
key={param.nome}
label={param.nome}
options={param.options}
value={paramInputs[param.nome] || ""}
value={paramInputs[param.nome] ?? ""}
onChange={(val) => handleInputChange(param.nome, val)}
placeholder={`Selecionar ${param.nome}`}
aria-label={param.nome}
/>
);
}
@@ -90,21 +134,23 @@ export default function GeradorCard({
key={param.nome}
type={param.tipo}
placeholder={param.placeholder || param.nome}
value={paramInputs[param.nome] || ""}
value={paramInputs[param.nome] ?? ""}
onChange={(e) => handleInputChange(param.nome, e.target.value)}
aria-label={param.nome}
/>
);
})}
</div>
)}
{/* Resultado + botão de copiar como ícone */}
<div className="mb-6 flex items-center gap-2 relative">
<ResultDisplay
value={resultado}
validated={validado}
onChange={handleResultadoChange}
aria-label={`${titulo} resultado`}
/>
{resultado && (
<Button
onClick={handleCopiar}
@@ -112,25 +158,31 @@ export default function GeradorCard({
variant="secondary"
className="flex items-center gap-1 px-2 py-1 text-xs h-auto"
title="Copiar"
>
</Button>
/>
)}
</div>
{/* Botões distribuídos numa linha com preenchimento igual */}
<div className="flex w-full gap-x-2">
<Button
onClick={handleGerar}
icon={<RefreshCwIcon className="w-4 h-4" />}
variant="primary"
className="flex-1"
title={`Gerar ${titulo}`}
disabled={loading}
>
Gerar
{loading ? "A gerar..." : "Gerar"}
</Button>
{onValidar && (
<Button onClick={handleValidar} variant="danger" className="flex-1">
Validar
<Button
onClick={handleValidar}
variant={validado === true ? "primary" : "danger"}
className="flex-1"
title={`Validar ${titulo}`}
disabled={loading || !resultado}
>
{loading ? "Verificando..." : "Validar"}
</Button>
)}
</div>

View File

@@ -1,6 +1,5 @@
import React from "react";
import GeradorCard from "../GeradorCard";
import { EnumToOptions } from "../../../library/utils";
import GeradorCard, { type Parametro } from "../GeradorCard";
import { EnumToOptions, getEnumKeyByValue } from "../../../library/utils";
import { NIFType } from "../../../service/api";
import GeradorService from "../../../Api/GeradorApi";
@@ -10,31 +9,31 @@ const NifTypes = EnumToOptions(NIFType).map((opt) => ({
}));
const GenerateNIF = () => {
const handleGenerateNIF = async (params: Record<string, string>) => {
const tipoSelecionado = params["Tipo de NIF"];
const nif = await GeradorService.GenerateNIF(tipoSelecionado);
const handleGenerateNIF = async (params?: Record<string, string>) => {
const tipoSelecionado = params?.["Tipo de NIF"] ?? String(NIFType.PessoaSingular2);
const nif = String(await GeradorService.GenerateNIF(tipoSelecionado));
return nif;
};
const handleValidateNIF = async (valor: string) => {
// Lógica real de validação
const bol = await GeradorService.ValidateNIF(valor);
const bol = Boolean(await GeradorService.ValidateNIF(valor));
return bol;
};
const parametros: Parametro[] = [
{
nome: "Tipo de NIF",
tipo: "select",
options: NifTypes,
default: getEnumKeyByValue(NIFType, NIFType.PessoaSingular2)
},
];
return (
<div className="max-w-md mx-auto p-6">
<GeradorCard
titulo="NIF"
parametros={[
{
nome: "Tipo de NIF",
tipo: "select",
options: NifTypes,
},
]}
parametros={parametros}
onGerar={handleGenerateNIF}
onValidar={handleValidateNIF}
/>

View File

@@ -1,4 +1,3 @@
import { useEffect, useState } from "react";
import GeradorService from "../../../Api/GeradorApi";
import GeradorCard from "../GeradorCard.tsx";

View File

@@ -25,7 +25,7 @@ export default function Footer() {
</Link>
</li>
<li>
<Link to="/geradores" className="hover:text-blue-500 dark:hover:text-blue-400 transition-colors duration-200">
<Link to="/home" className="hover:text-blue-500 dark:hover:text-blue-400 transition-colors duration-200">
Geradores
</Link>
</li>

View File

@@ -10,8 +10,9 @@ const navItems = [
{
name: "Geradores",
subItems: [
{ name: "Gerador de Senhas", path: "/geradores/senhas" },
{ name: "Gerador de Nomes", path: "/geradores/nomes" },
{ name: "Gerador de NIFs", path: "/NIF", },
{ name: "Gerador de NISS", path: "/NISS" },
{ name: "Gerador de CC", path: "/CC" },
],
},
{ name: "Contacto", path: "/contacto" },

View File

@@ -1,7 +1,6 @@
// components/layout/Layout.tsx
import Footer from './Footer';
import Header from './Header';
import { useTheme } from "../context/ThemeContext"; // caminho conforme a tua estrutura
export const Layout = ({ children }: { children: React.ReactNode }) => {

View File

@@ -1,18 +1,24 @@
const Utils = {
EnumToOptions,
// Outras fun<75><6E>es podem ser adicionadas aqui
// Outras fun<75><6E>es podem ser adicionadas aqui
};
export function EnumToOptions<T extends Record<string, string>>(enumObj: T): any[] {
return Object.entries(enumObj)
.filter(([key]) => isNaN(Number(key))) // Ignora entradas num<75>ricas, caso existam
.filter(([key]) => isNaN(Number(key))) // Ignora entradas num<75>ricas, caso existam
.map(([key, value]) => ({
value: key, // A chave do enum como `value`
label: value, // O valor do enum como `label`
}));
}
export function getEnumKeyByValue<T extends Record<string, string>>(
enumObj: T,
value: string
): string | undefined {
return Object.keys(enumObj).find((key) => enumObj[key as keyof T] === value);
}

View File

@@ -1,6 +1,3 @@
import React from "react";
export default function Blank() {
console.log("inicalizar componente");
return (

View File

@@ -0,0 +1,31 @@
import { useEffect } from "react";
import { useLocation } from "react-router";
import GenerateCC from "../components/Geradores/Tipos/GenerateCC";
export default function CC() {
const location = useLocation();
// Efeito para rolar até a seção correspondente ao path
useEffect(() => {
const featuresSection = document.getElementById(location.hash.substring(1));
if (featuresSection) {
featuresSection.scrollIntoView({ behavior: "smooth" });
}
}, [location]);
return (
<div >
<div >
{/* Seção de Funcionalidades */}
<section id="features" className="py-16 px-4 md:px-8">
<div className="max-w-6xl mx-auto grid grid-cols-1">
<div id="nif">
<GenerateCC />
</div>
</div>
</section>
</div>
</div>
);
}

View File

@@ -0,0 +1,31 @@
import { useEffect } from "react";
import { useLocation } from "react-router";
import GenerateNIF from "../components/Geradores/Tipos/GenerateNIF";
export default function NIF() {
const location = useLocation();
// Efeito para rolar até a seção correspondente ao path
useEffect(() => {
const featuresSection = document.getElementById(location.hash.substring(1));
if (featuresSection) {
featuresSection.scrollIntoView({ behavior: "smooth" });
}
}, [location]);
return (
<div >
<div >
{/* Seção de Funcionalidades */}
<section id="features" className="py-16 px-4 md:px-8">
<div className="max-w-6xl mx-auto grid grid-cols-1">
<div id="nif">
<GenerateNIF />
</div>
</div>
</section>
</div>
</div>
);
}

View File

@@ -0,0 +1,31 @@
import { useEffect } from "react";
import { useLocation } from "react-router";
import GenerateNISS from "../components/Geradores/Tipos/GenerateNISS";
export default function NISS() {
const location = useLocation();
// Efeito para rolar até a seção correspondente ao path
useEffect(() => {
const featuresSection = document.getElementById(location.hash.substring(1));
if (featuresSection) {
featuresSection.scrollIntoView({ behavior: "smooth" });
}
}, [location]);
return (
<div >
<div >
{/* Seção de Funcionalidades */}
<section id="features" className="py-16 px-4 md:px-8">
<div className="max-w-6xl mx-auto grid grid-cols-1">
<div id="nif">
<GenerateNISS />
</div>
</div>
</section>
</div>
</div>
);
}

View File

@@ -1,5 +1,6 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
@@ -10,12 +11,19 @@
*/
export enum NIFType {
/** Pessoa singular (1) */
PessoaSingular1 = "Pessoa singular (1)",
/** Pessoa singular (2) */
PessoaSingular2 = "Pessoa singular (2)",
/** Pessoa singular (3; novo em 2019) */
PessoaSingular3 = "Pessoa singular (3; novo em 2019)",
/** Pessoa colectiva (5) */
PessoaColectiva = "Pessoa colectiva (5)",
/** Pessoa colectiva pública (6) */
PessoaColectivaPublica = "Pessoa colectiva pública (6)",
/** Empresário em nome individual (8) */
EmpresarioIndividual = "Empresário em nome individual (8)",
/** Pessoa colectiva irregular ou número provisório (9) */
PessoaColectivaIrregular = "Pessoa colectiva irregular ou número provisório (9)",
}
@@ -29,10 +37,19 @@ export interface WeatherForecast {
summary?: string | null;
}
export type QueryParamsType = Record<string | number, any>;
export type ResponseFormat = keyof Omit<Body, "body" | "bodyUsed">;
import type {
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
HeadersDefaults,
ResponseType,
} from "axios";
import axios from "axios";
export interface FullRequestParams extends Omit<RequestInit, "body"> {
export type QueryParamsType = Record<string | number, any>;
export interface FullRequestParams
extends Omit<AxiosRequestConfig, "data" | "params" | "url" | "responseType"> {
/** set parameter to `true` for call `securityWorker` for this request */
secure?: boolean;
/** request path */
@@ -42,199 +59,155 @@ export interface FullRequestParams extends Omit<RequestInit, "body"> {
/** query params */
query?: QueryParamsType;
/** format of response (i.e. response.json() -> format: "json") */
format?: ResponseFormat;
format?: ResponseType;
/** request body */
body?: unknown;
/** base url */
baseUrl?: string;
/** request cancellation token */
cancelToken?: CancelToken;
}
export type RequestParams = Omit<FullRequestParams, "body" | "method" | "query" | "path">;
export type RequestParams = Omit<
FullRequestParams,
"body" | "method" | "query" | "path"
>;
export interface ApiConfig<SecurityDataType = unknown> {
baseUrl?: string;
baseApiParams?: Omit<RequestParams, "baseUrl" | "cancelToken" | "signal">;
securityWorker?: (securityData: SecurityDataType | null) => Promise<RequestParams | void> | RequestParams | void;
customFetch?: typeof fetch;
export interface ApiConfig<SecurityDataType = unknown>
extends Omit<AxiosRequestConfig, "data" | "cancelToken"> {
securityWorker?: (
securityData: SecurityDataType | null,
) => Promise<AxiosRequestConfig | void> | AxiosRequestConfig | void;
secure?: boolean;
format?: ResponseType;
}
export interface HttpResponse<D extends unknown, E extends unknown = unknown> extends Response {
data: D;
error: E;
}
type CancelToken = Symbol | string | number;
export enum ContentType {
Json = "application/json",
JsonApi = "application/vnd.api+json",
FormData = "multipart/form-data",
UrlEncoded = "application/x-www-form-urlencoded",
Text = "text/plain",
}
export class HttpClient<SecurityDataType = unknown> {
public baseUrl: string = "";
public instance: AxiosInstance;
private securityData: SecurityDataType | null = null;
private securityWorker?: ApiConfig<SecurityDataType>["securityWorker"];
private abortControllers = new Map<CancelToken, AbortController>();
private customFetch = (...fetchParams: Parameters<typeof fetch>) => fetch(...fetchParams);
private secure?: boolean;
private format?: ResponseType;
private baseApiParams: RequestParams = {
credentials: "same-origin",
headers: {},
redirect: "follow",
referrerPolicy: "no-referrer",
};
constructor(apiConfig: ApiConfig<SecurityDataType> = {}) {
Object.assign(this, apiConfig);
constructor({
securityWorker,
secure,
format,
...axiosConfig
}: ApiConfig<SecurityDataType> = {}) {
this.instance = axios.create({
...axiosConfig,
baseURL: axiosConfig.baseURL || "",
});
this.secure = secure;
this.format = format;
this.securityWorker = securityWorker;
}
public setSecurityData = (data: SecurityDataType | null) => {
this.securityData = data;
};
protected encodeQueryParam(key: string, value: any) {
const encodedKey = encodeURIComponent(key);
return `${encodedKey}=${encodeURIComponent(typeof value === "number" ? value : `${value}`)}`;
}
protected mergeRequestParams(
params1: AxiosRequestConfig,
params2?: AxiosRequestConfig,
): AxiosRequestConfig {
const method = params1.method || (params2 && params2.method);
protected addQueryParam(query: QueryParamsType, key: string) {
return this.encodeQueryParam(key, query[key]);
}
protected addArrayQueryParam(query: QueryParamsType, key: string) {
const value = query[key];
return value.map((v: any) => this.encodeQueryParam(key, v)).join("&");
}
protected toQueryString(rawQuery?: QueryParamsType): string {
const query = rawQuery || {};
const keys = Object.keys(query).filter((key) => "undefined" !== typeof query[key]);
return keys
.map((key) => (Array.isArray(query[key]) ? this.addArrayQueryParam(query, key) : this.addQueryParam(query, key)))
.join("&");
}
protected addQueryParams(rawQuery?: QueryParamsType): string {
const queryString = this.toQueryString(rawQuery);
return queryString ? `?${queryString}` : "";
}
private contentFormatters: Record<ContentType, (input: any) => any> = {
[ContentType.Json]: (input: any) =>
input !== null && (typeof input === "object" || typeof input === "string") ? JSON.stringify(input) : input,
[ContentType.Text]: (input: any) => (input !== null && typeof input !== "string" ? JSON.stringify(input) : input),
[ContentType.FormData]: (input: any) =>
Object.keys(input || {}).reduce((formData, key) => {
const property = input[key];
formData.append(
key,
property instanceof Blob
? property
: typeof property === "object" && property !== null
? JSON.stringify(property)
: `${property}`,
);
return formData;
}, new FormData()),
[ContentType.UrlEncoded]: (input: any) => this.toQueryString(input),
};
protected mergeRequestParams(params1: RequestParams, params2?: RequestParams): RequestParams {
return {
...this.baseApiParams,
...this.instance.defaults,
...params1,
...(params2 || {}),
headers: {
...(this.baseApiParams.headers || {}),
...((method &&
this.instance.defaults.headers[
method.toLowerCase() as keyof HeadersDefaults
]) ||
{}),
...(params1.headers || {}),
...((params2 && params2.headers) || {}),
},
};
}
protected createAbortSignal = (cancelToken: CancelToken): AbortSignal | undefined => {
if (this.abortControllers.has(cancelToken)) {
const abortController = this.abortControllers.get(cancelToken);
if (abortController) {
return abortController.signal;
protected stringifyFormItem(formItem: unknown) {
if (typeof formItem === "object" && formItem !== null) {
return JSON.stringify(formItem);
} else {
return `${formItem}`;
}
return void 0;
}
const abortController = new AbortController();
this.abortControllers.set(cancelToken, abortController);
return abortController.signal;
};
public abortRequest = (cancelToken: CancelToken) => {
const abortController = this.abortControllers.get(cancelToken);
if (abortController) {
abortController.abort();
this.abortControllers.delete(cancelToken);
protected createFormData(input: Record<string, unknown>): FormData {
if (input instanceof FormData) {
return input;
}
};
return Object.keys(input || {}).reduce((formData, key) => {
const property = input[key];
const propertyContent: any[] =
property instanceof Array ? property : [property];
public request = async <T = any, E = any>({
body,
for (const formItem of propertyContent) {
const isFileType = formItem instanceof Blob || formItem instanceof File;
formData.append(
key,
isFileType ? formItem : this.stringifyFormItem(formItem),
);
}
return formData;
}, new FormData());
}
public request = async <T = any, _E = any>({
secure,
path,
type,
query,
format,
baseUrl,
cancelToken,
body,
...params
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
}: FullRequestParams): Promise<AxiosResponse<T>> => {
const secureParams =
((typeof secure === "boolean" ? secure : this.baseApiParams.secure) &&
((typeof secure === "boolean" ? secure : this.secure) &&
this.securityWorker &&
(await this.securityWorker(this.securityData))) ||
{};
const requestParams = this.mergeRequestParams(params, secureParams);
const queryString = query && this.toQueryString(query);
const payloadFormatter = this.contentFormatters[type || ContentType.Json];
const responseFormat = format || requestParams.format;
const responseFormat = format || this.format || undefined;
return this.customFetch(`${baseUrl || this.baseUrl || ""}${path}${queryString ? `?${queryString}` : ""}`, {
if (
type === ContentType.FormData &&
body &&
body !== null &&
typeof body === "object"
) {
body = this.createFormData(body as Record<string, unknown>);
}
if (
type === ContentType.Text &&
body &&
body !== null &&
typeof body !== "string"
) {
body = JSON.stringify(body);
}
return this.instance.request({
...requestParams,
headers: {
...(requestParams.headers || {}),
...(type && type !== ContentType.FormData ? { "Content-Type": type } : {}),
...(type ? { "Content-Type": type } : {}),
},
signal: (cancelToken ? this.createAbortSignal(cancelToken) : requestParams.signal) || null,
body: typeof body === "undefined" || body === null ? null : payloadFormatter(body),
}).then(async (response) => {
const r = response.clone() as HttpResponse<T, E>;
r.data = null as unknown as T;
r.error = null as unknown as E;
const data = !responseFormat
? r
: await response[responseFormat]()
.then((data) => {
if (r.ok) {
r.data = data;
} else {
r.error = data;
}
return r;
})
.catch((e) => {
r.error = e;
return r;
});
if (cancelToken) {
this.abortControllers.delete(cancelToken);
}
if (!response.ok) throw data;
return data;
params: query,
responseType: responseFormat,
data: body,
url: path,
});
};
}
@@ -244,7 +217,9 @@ export class HttpClient<SecurityDataType = unknown> {
* @version v1
* @contact Marco Santos
*/
export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDataType> {
export class Api<
SecurityDataType extends unknown,
> extends HttpClient<SecurityDataType> {
generate = {
/**
* No description
@@ -255,14 +230,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
*/
generateNifList: (
query?: {
type?:
| "Pessoa singular (1)"
| "Pessoa singular (2)"
| "Pessoa singular (3; novo em 2019)"
| "Pessoa colectiva (5)"
| "Pessoa colectiva pública (6)"
| "Empresário em nome individual (8)"
| "Pessoa colectiva irregular ou número provisório (9)";
type?: NIFType;
},
params: RequestParams = {},
) =>
@@ -330,6 +298,21 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
format: "json",
...params,
}),
/**
* No description
*
* @tags Generate
* @name GenerateCcList
* @request GET:/Generate/GenerateCC
*/
generateCcList: (params: RequestParams = {}) =>
this.request<string, any>({
path: `/Generate/GenerateCC`,
method: "GET",
format: "json",
...params,
}),
};
weatherForecast = {
/**