Merge pull request 'Novo Layout' (#1) from newlayout into master
Reviewed-on: Marco/Geradores#1
This commit is contained in:
@@ -1,46 +1,81 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace GeradoresService
|
namespace GeradoresService
|
||||||
{
|
{
|
||||||
internal class CC
|
public static class CartaoCidadao
|
||||||
{
|
{
|
||||||
|
// Função estática para gerar o número de documento
|
||||||
/*static bool Validate(string CardNumber)
|
public static string Generate()
|
||||||
{
|
{
|
||||||
int sum = 0;
|
Random rand = new Random();
|
||||||
bool alternate = false;
|
|
||||||
|
|
||||||
// Percorre os dígitos do número de trás para frente
|
// Gerar o Número de Identificação Civil (8 dígitos)
|
||||||
for (int i = creditCardNumber.Length - 1; i >= 0; i--)
|
string numeroIdentificacaoCivil = rand.Next(0, 100000000).ToString("D8");
|
||||||
|
|
||||||
|
// Gerar o Check Digit C
|
||||||
|
int checkDigitC = CalcularCheckDigit(numeroIdentificacaoCivil);
|
||||||
|
|
||||||
|
// Gerar a Versão (duas letras ou números aleatórios entre A-Z ou 0-9)
|
||||||
|
string versao = GerarVersao();
|
||||||
|
|
||||||
|
// Gerar o Check Digit T (Número aleatório entre 0-9)
|
||||||
|
int checkDigitT = rand.Next(0, 10); // T: Número aleatório por enquanto, conforme exemplo.
|
||||||
|
|
||||||
|
// Montar o número completo do documento com o formato desejado
|
||||||
|
string numeroDocumento = $"{numeroIdentificacaoCivil} {checkDigitC} {versao}{checkDigitT}";
|
||||||
|
|
||||||
|
return numeroDocumento;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para calcular o Check Digit C com base no Número de Identificação Civil
|
||||||
|
private static int CalcularCheckDigit(string numeroIdentificacaoCivil)
|
||||||
|
{
|
||||||
|
int soma = 0;
|
||||||
|
bool segundoElemento = false;
|
||||||
|
|
||||||
|
// Percorrer o número da direita para a esquerda
|
||||||
|
for (int i = numeroIdentificacaoCivil.Length - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
int digit = creditCardNumber[i] - '0';
|
int valor = numeroIdentificacaoCivil[i] - '0'; // Converte o char para inteiro
|
||||||
|
|
||||||
// Se é um dígito
|
// Multiplicar cada segundo número por 2
|
||||||
if (digit >= 0 && digit <= 9)
|
if (segundoElemento)
|
||||||
{
|
{
|
||||||
if (alternate)
|
valor *= 2;
|
||||||
{
|
if (valor >= 10) valor -= 9; // Subtrair 9 se o valor for maior ou igual a 10
|
||||||
digit *= 2;
|
}
|
||||||
if (digit > 9)
|
|
||||||
digit -= 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
sum += digit;
|
soma += valor;
|
||||||
alternate = !alternate;
|
segundoElemento = !segundoElemento;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retornar o check digit (resto da soma por 10)
|
||||||
|
return soma % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Função para gerar a versão (letras ou números)
|
||||||
|
private static string GerarVersao()
|
||||||
|
{
|
||||||
|
Random rand = new Random();
|
||||||
|
string versao = "";
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) // Gerar duas letras/números
|
||||||
|
{
|
||||||
|
int numeroAleatorio = rand.Next(0, 36); // 26 letras + 10 números
|
||||||
|
|
||||||
|
if (numeroAleatorio < 26)
|
||||||
|
{
|
||||||
|
// Gerar uma letra de A a Z
|
||||||
|
versao += (char)('A' + numeroAleatorio);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Se não é um dígito, o número é inválido
|
// Gerar um número de 0 a 9
|
||||||
return false;
|
versao += (char)('0' + (numeroAleatorio - 26));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// O número de cartão de crédito é válido se a soma for divisível por 10
|
return versao;
|
||||||
return sum % 10 == 0;
|
}
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using GeradoresService.DAL;
|
using GeradoresService.DAL;
|
||||||
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Net.NetworkInformation;
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace GeradoresService
|
namespace GeradoresService
|
||||||
{
|
{
|
||||||
@@ -13,6 +15,7 @@ namespace GeradoresService
|
|||||||
_geradoresContext = geradoresContext;
|
_geradoresContext = geradoresContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
public enum NIFType
|
public enum NIFType
|
||||||
{
|
{
|
||||||
[Description("Pessoa singular (1)")]
|
[Description("Pessoa singular (1)")]
|
||||||
@@ -37,102 +40,58 @@ namespace GeradoresService
|
|||||||
PessoaColectivaIrregular = 9
|
PessoaColectivaIrregular = 9
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Generate(string? type)
|
public string Generate(NIFType? type)
|
||||||
{
|
{
|
||||||
var nif = GenerateRandomNIF(type);
|
var nif = GenerateRandomNIF(type);
|
||||||
//SaveNIF(nif);
|
|
||||||
return nif;
|
return nif;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GenerateRandomNIF(string? nifType)
|
public string GenerateRandomNIF(NIFType? nifType)
|
||||||
{
|
{
|
||||||
var firstDigitValidate = new char[] { '1', '2', '3', '5', '6', '8', '9' };
|
|
||||||
Random rnd = new Random();
|
|
||||||
char firstDigit;
|
char firstDigit;
|
||||||
|
Random rnd = new Random();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(nifType))
|
if (nifType.HasValue)
|
||||||
{
|
{
|
||||||
// Gera o primeiro dígito aleatório dentro dos válidos
|
firstDigit = ((int)nifType.Value).ToString()[0];
|
||||||
int firstDigitIndex = rnd.Next(0, 6); // Escolhe um índice de 0 a 5
|
|
||||||
firstDigit = firstDigitValidate[firstDigitIndex];
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (firstDigitValidate.Contains(nifType[0]))
|
var validFirstDigits = Enum.GetValues(typeof(NIFType)).Cast<int>().Select(v => v.ToString()[0]).ToArray();
|
||||||
{
|
firstDigit = validFirstDigits[rnd.Next(validFirstDigits.Length)];
|
||||||
firstDigit = nifType[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int firstDigitIndex = rnd.Next(0, 6); // Escolhe um índice de 0 a 5
|
|
||||||
firstDigit = firstDigitValidate[firstDigitIndex];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gera os próximos 7 dígitos aleatórios
|
string nextDigits = string.Concat(Enumerable.Range(0, 7).Select(_ => rnd.Next(0, 10).ToString()));
|
||||||
string nextDigits = "";
|
|
||||||
|
int checkDigit = (firstDigit - '0') * 9;
|
||||||
for (int i = 0; i < 7; i++)
|
for (int i = 0; i < 7; i++)
|
||||||
{
|
{
|
||||||
nextDigits += rnd.Next(0, 10); // Gera um dígito aleatório de 0 a 9
|
checkDigit += (nextDigits[i] - '0') * (8 - i);
|
||||||
}
|
|
||||||
|
|
||||||
// Calcula o dígito de controlo
|
|
||||||
int checkDigit = (firstDigit - '0') * 9;
|
|
||||||
for (int i = 2; i <= 8; i++)
|
|
||||||
{
|
|
||||||
checkDigit += (nextDigits[i - 2] - '0') * (10 - i);
|
|
||||||
}
|
}
|
||||||
checkDigit = 11 - (checkDigit % 11);
|
checkDigit = 11 - (checkDigit % 11);
|
||||||
if (checkDigit >= 10)
|
if (checkDigit >= 10)
|
||||||
checkDigit = 0;
|
checkDigit = 0;
|
||||||
|
|
||||||
// Concatena os dígitos gerados e o dígito de controlo
|
return firstDigit + nextDigits + checkDigit;
|
||||||
string randomNIF = firstDigit + nextDigits + checkDigit;
|
|
||||||
|
|
||||||
return randomNIF;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Validate(string nif)
|
public bool Validate(string nif)
|
||||||
{
|
{
|
||||||
// Verificar se o NIF tem 9 dígitos
|
if (nif.Length != 9 || !nif.All(char.IsDigit))
|
||||||
if (nif.Length != 9)
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// Converter o NIF para um array de dígitos
|
int[] digitos = nif.Select(c => c - '0').ToArray();
|
||||||
int[] digitos = new int[9];
|
int soma = digitos.Take(8).Select((num, i) => num * (9 - i)).Sum();
|
||||||
for (int i = 0; i < 9; i++)
|
int digitoControlo = (11 - (soma % 11)) % 10;
|
||||||
{
|
|
||||||
if (!int.TryParse(nif[i].ToString(), out digitos[i]))
|
|
||||||
{
|
|
||||||
return false; // Se algum caractere não for um dígito, o NIF é inválido
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calcular o dígito de controlo
|
|
||||||
int soma = 0;
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
|
||||||
soma += digitos[i] * (9 - i);
|
|
||||||
}
|
|
||||||
int resto = soma % 11;
|
|
||||||
int digitoControlo = resto <= 1 ? 0 : 11 - resto;
|
|
||||||
|
|
||||||
// Verificar se o dígito de controlo coincide com o último dígito do NIF
|
|
||||||
return digitoControlo == digitos[8];
|
return digitoControlo == digitos[8];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SaveNIF(string NIF)
|
public void SaveNIF(string NIF)
|
||||||
{
|
{
|
||||||
var ger = new Geradore()
|
var ger = new Geradore() { Valor = NIF };
|
||||||
{
|
|
||||||
Valor = NIF
|
|
||||||
};
|
|
||||||
|
|
||||||
_geradoresContext.Geradores.Add(ger);
|
_geradoresContext.Geradores.Add(ger);
|
||||||
_geradoresContext.SaveChanges();
|
_geradoresContext.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,14 +15,8 @@ namespace GeradoresWS.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region NIF
|
#region NIF
|
||||||
[HttpGet("GetNIFTypes")]
|
|
||||||
public List<NIF.NIFType> GetNIFTypes()
|
|
||||||
{
|
|
||||||
return new List<NIF.NIFType>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("GenerateNIF")]
|
[HttpGet("GenerateNIF")]
|
||||||
public string GenerateNIF(string? type)
|
public string GenerateNIF(NIF.NIFType type)
|
||||||
{
|
{
|
||||||
var nif = new NIF(_context);
|
var nif = new NIF(_context);
|
||||||
return nif.Generate(type);
|
return nif.Generate(type);
|
||||||
@@ -49,5 +43,13 @@ namespace GeradoresWS.Controllers
|
|||||||
return NISS.Validate(nif);
|
return NISS.Validate(nif);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region CC
|
||||||
|
[HttpGet("GenerateCC")]
|
||||||
|
public string GenerateCC()
|
||||||
|
{
|
||||||
|
return CartaoCidadao.Generate();
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
65
GeradoresWS/Filters/EnumDescriptionFilter.cs
Normal file
65
GeradoresWS/Filters/EnumDescriptionFilter.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Microsoft.OpenApi.Any;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace Primus.ProfitabilityModel.WebAPI.Filters
|
||||||
|
{
|
||||||
|
public class EnumDescriptionFilter : ISchemaFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
|
||||||
|
{
|
||||||
|
if (context.Type.IsEnum)
|
||||||
|
{
|
||||||
|
// Obter os valores do enum
|
||||||
|
var enumValues = Enum.GetValues(context.Type)
|
||||||
|
.Cast<Enum>()
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Obter os nomes dos enums
|
||||||
|
var enumNames = enumValues.Select(e => e.ToString()).ToList();
|
||||||
|
|
||||||
|
// Obter as descrições dos enums
|
||||||
|
var enumDescriptions = enumValues.Select(e => GetEnumDescription(e)).ToList();
|
||||||
|
|
||||||
|
// Adicionar as extensões com os nomes e descrições
|
||||||
|
var enumNamesArray = new OpenApiArray();
|
||||||
|
foreach (var name in enumNames)
|
||||||
|
{
|
||||||
|
enumNamesArray.Add(new OpenApiString(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
var enumDescriptionsArray = new OpenApiArray();
|
||||||
|
foreach (var description in enumDescriptions)
|
||||||
|
{
|
||||||
|
enumDescriptionsArray.Add(new OpenApiString(description));
|
||||||
|
}
|
||||||
|
|
||||||
|
schema.Extensions.Add("x-enumNames", enumDescriptionsArray);
|
||||||
|
schema.Extensions.Add("x-enumDescriptions", enumDescriptionsArray);
|
||||||
|
|
||||||
|
// Adicionar os valores do enum ao campo Enum
|
||||||
|
schema.Enum = enumNames
|
||||||
|
.Select(name => new OpenApiString(name))
|
||||||
|
.Cast<IOpenApiAny>()
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetEnumDescription(Enum value)
|
||||||
|
{
|
||||||
|
// Verificar se o campo é nulo antes de chamar GetCustomAttribute
|
||||||
|
var field = value.GetType().GetField(value.ToString());
|
||||||
|
if (field == null)
|
||||||
|
{
|
||||||
|
return value.ToString(); // Se o campo for nulo, retorna o nome do valor do enum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acessa o atributo Description, caso exista
|
||||||
|
var attribute = (DescriptionAttribute)Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute));
|
||||||
|
|
||||||
|
// Retorna a descrição ou o nome do valor do enum, se a descrição não estiver presente
|
||||||
|
return attribute?.Description ?? value.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,35 @@
|
|||||||
using GeradoresService.DAL;
|
using GeradoresService.DAL;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Primus.ProfitabilityModel.WebAPI.Filters;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
|
builder.Services.AddControllers().AddJsonOptions(options =>
|
||||||
builder.Services.AddControllers();
|
{
|
||||||
|
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||||
|
});
|
||||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen();
|
builder.Services.AddSwaggerGen(options =>
|
||||||
|
{
|
||||||
|
options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
|
||||||
|
options.SwaggerDoc("v1", new OpenApiInfo
|
||||||
|
{
|
||||||
|
|
||||||
|
Version = "v1",
|
||||||
|
Title = "FactoryId",
|
||||||
|
Contact = new OpenApiContact
|
||||||
|
{
|
||||||
|
Name = "Marco Santos"
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Registra o filtro de esquema que aplica descri<72><69>es para todos os enums
|
||||||
|
options.SchemaFilter<EnumDescriptionFilter>();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
var connectionString = builder.Configuration.GetConnectionString("GeradoresConnection");
|
var connectionString = builder.Configuration.GetConnectionString("GeradoresConnection");
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
# Geradores
|
# Geradores
|
||||||
|
|
||||||
|
<code> docker build -t shini89/geradoresws ..</code>
|
||||||
|
<code> docker run --rm -p 44329:8080 shini89/geradoresws</code>
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ namespace UnitTest
|
|||||||
public void GenerateNIF()
|
public void GenerateNIF()
|
||||||
{
|
{
|
||||||
var nif = new NIF(_context);
|
var nif = new NIF(_context);
|
||||||
var gerenatednif = nif.Generate("2");
|
//var gerenatednif = nif.Generate("2");
|
||||||
Assert.NotEmpty(gerenatednif);
|
//Assert.NotEmpty(gerenatednif);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
19
UnitTest/Geradores.cs
Normal file
19
UnitTest/Geradores.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using GeradoresService;
|
||||||
|
using GeradoresService.DAL;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace UnitTest
|
||||||
|
{
|
||||||
|
public class Geradores
|
||||||
|
{
|
||||||
|
private readonly GeradoresContext _context;
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GenerateCC()
|
||||||
|
{
|
||||||
|
var cc = CartaoCidadao.Generate();
|
||||||
|
Console.WriteLine(cc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
REACT_APP_API_URL=https://localhost:44329/
|
VITE_API_URL=http://localhost:44329/
|
||||||
44
geradoresfe/.gitignore
vendored
44
geradoresfe/.gitignore
vendored
@@ -1,28 +1,24 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# Logs
|
||||||
|
logs
|
||||||
# dependencies
|
*.log
|
||||||
/node_modules
|
|
||||||
/.pnp
|
|
||||||
.pnp.js
|
|
||||||
|
|
||||||
# testing
|
|
||||||
/coverage
|
|
||||||
|
|
||||||
# production
|
|
||||||
/build
|
|
||||||
|
|
||||||
# misc
|
|
||||||
.DS_Store
|
|
||||||
.env.local
|
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.vercel
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
package-lock.json
|
node_modules
|
||||||
yarn-lock.json
|
dist
|
||||||
pnpm-lock.json
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|||||||
@@ -1,46 +1,69 @@
|
|||||||
# Getting Started with Create React App
|
# React + TypeScript + Vite
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||||
|
|
||||||
## Available Scripts
|
Currently, two official plugins are available:
|
||||||
|
|
||||||
In the project directory, you can run:
|
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||||
|
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
||||||
|
|
||||||
### `npm start`
|
## Expanding the ESLint configuration
|
||||||
|
|
||||||
Runs the app in the development mode.\
|
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
||||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|
||||||
|
|
||||||
The page will reload if you make edits.\
|
```js
|
||||||
You will also see any lint errors in the console.
|
export default tseslint.config([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
// Other configs...
|
||||||
|
|
||||||
### `npm test`
|
// Remove tseslint.configs.recommended and replace with this
|
||||||
|
...tseslint.configs.recommendedTypeChecked,
|
||||||
|
// Alternatively, use this for stricter rules
|
||||||
|
...tseslint.configs.strictTypeChecked,
|
||||||
|
// Optionally, add this for stylistic rules
|
||||||
|
...tseslint.configs.stylisticTypeChecked,
|
||||||
|
|
||||||
Launches the test runner in the interactive watch mode.\
|
// Other configs...
|
||||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
],
|
||||||
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
|
},
|
||||||
|
// other options...
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
```
|
||||||
|
|
||||||
### `npm run build`
|
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
```js
|
||||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
// eslint.config.js
|
||||||
|
import reactX from 'eslint-plugin-react-x'
|
||||||
|
import reactDom from 'eslint-plugin-react-dom'
|
||||||
|
|
||||||
The build is minified and the filenames include the hashes.\
|
export default tseslint.config([
|
||||||
Your app is ready to be deployed!
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
### `npm run eject`
|
// Other configs...
|
||||||
|
// Enable lint rules for React
|
||||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
reactX.configs['recommended-typescript'],
|
||||||
|
// Enable lint rules for React DOM
|
||||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
reactDom.configs.recommended,
|
||||||
|
],
|
||||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
languageOptions: {
|
||||||
|
parserOptions: {
|
||||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
||||||
|
tsconfigRootDir: import.meta.dirname,
|
||||||
## Learn More
|
},
|
||||||
|
// other options...
|
||||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
},
|
||||||
|
},
|
||||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
])
|
||||||
|
```
|
||||||
|
|||||||
23
geradoresfe/eslint.config.js
Normal file
23
geradoresfe/eslint.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import { globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default tseslint.config([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
reactHooks.configs['recommended-latest'],
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
@@ -19,5 +19,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="src\Api\" />
|
<Folder Include="src\Api\" />
|
||||||
|
<Folder Include="src\types\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
13
geradoresfe/index.html
Normal file
13
geradoresfe/index.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/logotipo.png" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Factory Id</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<configuration>
|
|
||||||
<packageSources>
|
|
||||||
<clear />
|
|
||||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
|
|
||||||
</packageSources>
|
|
||||||
<disabledPackageSources>
|
|
||||||
<clear />
|
|
||||||
</disabledPackageSources>
|
|
||||||
</configuration>
|
|
||||||
@@ -1,75 +1,43 @@
|
|||||||
{
|
{
|
||||||
"name": "tailadmin-react-free",
|
"name": "geradoresfe",
|
||||||
"version": "2.0.0",
|
"private": true,
|
||||||
"dependencies": {
|
"version": "1.0.0",
|
||||||
"@fullcalendar/core": "^6.1.15",
|
"type": "module",
|
||||||
"@fullcalendar/daygrid": "^6.1.15",
|
|
||||||
"@fullcalendar/interaction": "^6.1.15",
|
|
||||||
"@fullcalendar/list": "^6.1.15",
|
|
||||||
"@fullcalendar/react": "^6.1.15",
|
|
||||||
"@fullcalendar/timegrid": "^6.1.15",
|
|
||||||
"@react-jvectormap/core": "^1.0.4",
|
|
||||||
"@react-jvectormap/world": "^1.1.2",
|
|
||||||
"@tailwindcss/forms": "^0.5.9",
|
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
|
||||||
"@testing-library/react": "^13.4.0",
|
|
||||||
"@testing-library/user-event": "^13.5.0",
|
|
||||||
"@types/jest": "^27.5.2",
|
|
||||||
"@types/node": "^16.18.119",
|
|
||||||
"@types/react": "^18.3.12",
|
|
||||||
"@types/react-dom": "^18.3.1",
|
|
||||||
"apexcharts": "^4.1.0",
|
|
||||||
"autoprefixer": "^10.4.20",
|
|
||||||
"axios": "^1.8.1",
|
|
||||||
"classnames": "^2.5.1",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"flatpickr": "^4.6.13",
|
|
||||||
"lucide-react": "^0.477.0",
|
|
||||||
"motion": "^12.4.10",
|
|
||||||
"postcss": "^8.4.49",
|
|
||||||
"react": "^18.3.1",
|
|
||||||
"react-apexcharts": "^1.6.0",
|
|
||||||
"react-dnd": "^16.0.1",
|
|
||||||
"react-dnd-html5-backend": "^16.0.1",
|
|
||||||
"react-dom": "^18.3.1",
|
|
||||||
"react-dropzone": "^14.3.5",
|
|
||||||
"react-flatpickr": "^3.10.13",
|
|
||||||
"react-helmet": "^6.1.0",
|
|
||||||
"react-helmet-async": "^2.0.5",
|
|
||||||
"react-router": "^7.0.1",
|
|
||||||
"react-scripts": "5.0.1",
|
|
||||||
"react-simple-maps": "^3.0.0",
|
|
||||||
"swiper": "^11.1.15",
|
|
||||||
"tailwind-merge": "^2.6.0",
|
|
||||||
"typescript": "^4.9.5",
|
|
||||||
"web-vitals": "^2.1.4"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"dev": "vite",
|
||||||
"build": "react-scripts build",
|
"build": "tsc -b && vite build",
|
||||||
"test": "react-scripts test",
|
"lint": "eslint .",
|
||||||
"eject": "react-scripts eject"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"dependencies": {
|
||||||
"extends": [
|
"@headlessui/react": "^2.2.4",
|
||||||
"react-app",
|
"@heroicons/react": "^2.2.0",
|
||||||
"react-app/jest"
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
]
|
"axios": "^1.10.0",
|
||||||
},
|
"lucide-react": "^0.525.0",
|
||||||
"browserslist": {
|
"react": "^19.1.0",
|
||||||
"production": [
|
"react-dom": "^19.1.0",
|
||||||
">0.2%",
|
"react-hot-toast": "^2.5.2",
|
||||||
"not dead",
|
"react-router": "^7.6.3",
|
||||||
"not op_mini all"
|
"react-router-dom": "^7.6.3",
|
||||||
],
|
"tailwind-merge": "^3.3.1"
|
||||||
"development": [
|
|
||||||
"last 1 chrome version",
|
|
||||||
"last 1 firefox version",
|
|
||||||
"last 1 safari version"
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react-flatpickr": "^3.8.11",
|
"@eslint/js": "^9.30.1",
|
||||||
"tailwindcss": "^3.4.15"
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
|
"@types/node": "^24.0.13",
|
||||||
|
"@types/react": "^19.1.8",
|
||||||
|
"@types/react-dom": "^19.1.6",
|
||||||
|
"@vitejs/plugin-react": "^4.6.0",
|
||||||
|
"autoprefixer": "^10.4.21",
|
||||||
|
"eslint": "^9.30.1",
|
||||||
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
|
"globals": "^16.3.0",
|
||||||
|
"postcss": "^8.5.6",
|
||||||
|
"tailwindcss": "^4.1.11",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"typescript-eslint": "^8.35.1",
|
||||||
|
"vite": "^7.0.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
geradoresfe/postcss.config.cjs
Normal file
6
geradoresfe/postcss.config.cjs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: [
|
||||||
|
require('@tailwindcss/postcss'),
|
||||||
|
require('autoprefixer'),
|
||||||
|
],
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta
|
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
|
||||||
<!--
|
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
|
||||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
|
||||||
-->
|
|
||||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
|
||||||
<!--
|
|
||||||
Notice the use of %PUBLIC_URL% in the tags above.
|
|
||||||
It will be replaced with the URL of the `public` folder during the build.
|
|
||||||
Only files inside the `public` folder can be referenced from the HTML.
|
|
||||||
|
|
||||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
|
||||||
work correctly both with client-side routing and a non-root public URL.
|
|
||||||
Learn how to configure a non-root public URL by running `npm run build`.
|
|
||||||
-->
|
|
||||||
</head>
|
|
||||||
<body class="dark:bg-gray-900">
|
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
|
||||||
<div id="root"></div>
|
|
||||||
<!--
|
|
||||||
This HTML file is a template.
|
|
||||||
If you open it directly in the browser, you will see an empty page.
|
|
||||||
|
|
||||||
You can add webfonts, meta tags, or analytics to this file.
|
|
||||||
The build step will place the bundled scripts into the <body> tag.
|
|
||||||
|
|
||||||
To begin the development, run `npm start` or `yarn start`.
|
|
||||||
To create a production bundle, use `npm run build` or `yarn build`.
|
|
||||||
-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "React App",
|
|
||||||
"name": "Create React App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import axios, { AxiosResponse } from 'axios';
|
import axios from 'axios';
|
||||||
|
import type { AxiosResponse } from 'axios';
|
||||||
|
import { NIFType } from '../service/api';
|
||||||
|
import { toast } from 'react-hot-toast';
|
||||||
|
|
||||||
const API_URL = process.env.REACT_APP_API_URL;
|
const API_URL = import.meta.env.VITE_API_URL;
|
||||||
|
|
||||||
class GeradorService {
|
class GeradorService {
|
||||||
static async GenerateNIF(type: string | null): Promise<any[]> {
|
static async GenerateNIF(type: string | null): Promise<any[]> {
|
||||||
@@ -13,6 +16,24 @@ class GeradorService {
|
|||||||
});
|
});
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
||||||
|
toast.error('Error fetching NIF:' + error.message);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async ValidateNIF(nif: string | null): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const response: AxiosResponse = await axios.get(API_URL + 'Generate/ValidateNIF',
|
||||||
|
{
|
||||||
|
params: {
|
||||||
|
nif: nif
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
toast.error('Error fetching NIF:' + error);
|
||||||
|
|
||||||
console.error('Error fetching NIF:', error);
|
console.error('Error fetching NIF:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -28,6 +49,17 @@ class GeradorService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async GenerateCC(): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const response: AxiosResponse = await axios.get(API_URL + 'Generate/GenerateCC');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching NIF:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default GeradorService;
|
export default GeradorService;
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render, screen } from '@testing-library/react';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
render(<App />);
|
|
||||||
const linkElement = screen.getByText(/learn react/i);
|
|
||||||
expect(linkElement).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
@@ -1,37 +1,32 @@
|
|||||||
import { Route, BrowserRouter as Router, Routes } from "react-router";
|
import { Routes, Route, BrowserRouter as Router } from 'react-router-dom';
|
||||||
import AppLayout from "./layout/AppLayout";
|
import { Toaster } from 'react-hot-toast';
|
||||||
import AuthLayout from "./layout/AuthLayout";
|
import Home from './pages/Home';
|
||||||
import SignIn from "./pages/AuthPages/SignIn";
|
import { Layout } from './layout/Layout';
|
||||||
import SignUp from "./pages/AuthPages/SignUp";
|
import { useTheme } from './context/ThemeContext';
|
||||||
import Blank from "./pages/Blank";
|
|
||||||
|
|
||||||
import Home from "./pages/Home";
|
function App() {
|
||||||
import GenerateNIF from "./pages/GenerateNIF";
|
const { theme } = useTheme();
|
||||||
import GenerateNISS from "./pages/GenerateNISS";
|
|
||||||
import GenerateCC from "./pages/GenerateCC";
|
|
||||||
|
|
||||||
export default function App() {
|
return (
|
||||||
return (
|
<Router>
|
||||||
<>
|
<Layout>
|
||||||
<Router>
|
<Routes>
|
||||||
<Routes>
|
<Route path="/" element={<Home />} />
|
||||||
{/* Dashboard Layout */}
|
<Route path="/NIF" />
|
||||||
<Route element={<AppLayout />}>
|
<Route path="/NISS" />
|
||||||
<Route index path="/" element={<Home />} />
|
<Route path="/CC" />
|
||||||
<Route path="/NIF" element={<GenerateNIF />} />
|
</Routes>
|
||||||
<Route path="/NISS" element={<GenerateNISS />} />
|
<Toaster position="top-right"
|
||||||
<Route path="/CC" element={<GenerateCC />} />
|
toastOptions={{
|
||||||
</Route>
|
style: {
|
||||||
|
background: theme === 'dark' ? '#1f2937' : '#fff',
|
||||||
{/* Auth Layout */}
|
color: theme === 'dark' ? '#fff' : '#000',
|
||||||
<Route element={<AuthLayout />}>
|
},
|
||||||
<Route path="/signin" element={<SignIn />} />
|
}}
|
||||||
<Route path="/signup" element={<SignUp />} />
|
theme={theme} />
|
||||||
</Route>
|
</Layout>
|
||||||
|
</Router>
|
||||||
{/* Fallback Route */}
|
);
|
||||||
</Routes>
|
|
||||||
</Router>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default App;
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 156 KiB |
BIN
geradoresfe/src/assets/logotipo32x32.png
Normal file
BIN
geradoresfe/src/assets/logotipo32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 44 KiB |
1
geradoresfe/src/assets/react.svg
Normal file
1
geradoresfe/src/assets/react.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
139
geradoresfe/src/components/Geradores/GeradorCard.tsx
Normal file
139
geradoresfe/src/components/Geradores/GeradorCard.tsx
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import React, { 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';
|
||||||
|
|
||||||
|
type Parametro =
|
||||||
|
| { nome: string; tipo: "text" | "number" | "password"; placeholder?: string }
|
||||||
|
| { nome: string; tipo: "select"; options: { label: string; value: string }[] };
|
||||||
|
|
||||||
|
type GeradorCardProps = {
|
||||||
|
readonly titulo: string;
|
||||||
|
readonly descricao?: string;
|
||||||
|
readonly parametros?: Parametro[];
|
||||||
|
readonly onGerar: (parametros?: Record<string, string>) => string | Promise<string>;
|
||||||
|
readonly onValidar?: (valor: string) => boolean | Promise<boolean>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function GeradorCard({
|
||||||
|
titulo,
|
||||||
|
descricao,
|
||||||
|
parametros = [],
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCopiar = () => {
|
||||||
|
if (!resultado) return;
|
||||||
|
navigator.clipboard.writeText(resultado);
|
||||||
|
toast.success(`${titulo} copiado!`,
|
||||||
|
{
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResultadoChange = (valor: string) => {
|
||||||
|
setResultado(valor);
|
||||||
|
setValidado(null); // resetar estado de validação 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="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) => {
|
||||||
|
if (param.tipo === "select") {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
key={param.nome}
|
||||||
|
label={param.nome}
|
||||||
|
options={param.options}
|
||||||
|
value={paramInputs[param.nome] || ""}
|
||||||
|
onChange={(val) => handleInputChange(param.nome, val)}
|
||||||
|
placeholder={`Selecionar ${param.nome}`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
key={param.nome}
|
||||||
|
type={param.tipo}
|
||||||
|
placeholder={param.placeholder || param.nome}
|
||||||
|
value={paramInputs[param.nome] || ""}
|
||||||
|
onChange={(e) => handleInputChange(param.nome, e.target.value)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</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}
|
||||||
|
/>
|
||||||
|
{resultado && (
|
||||||
|
<Button
|
||||||
|
onClick={handleCopiar}
|
||||||
|
icon={<CopyIcon className="w-4 h-4" />}
|
||||||
|
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"
|
||||||
|
>
|
||||||
|
Gerar
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
{onValidar && (
|
||||||
|
<Button onClick={handleValidar} variant="danger" className="flex-1">
|
||||||
|
Validar
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
29
geradoresfe/src/components/Geradores/Tipos/GenerateCC.tsx
Normal file
29
geradoresfe/src/components/Geradores/Tipos/GenerateCC.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import GeradorService from "../../../Api/GeradorApi.tsx";
|
||||||
|
import GeradorCard from "../GeradorCard.tsx";
|
||||||
|
|
||||||
|
const GenerateCC = () => {
|
||||||
|
|
||||||
|
const handleGenerateCC = async () => {
|
||||||
|
const cc = await GeradorService.GenerateCC()
|
||||||
|
return cc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidateCC = async (valor: string) => {
|
||||||
|
// Lógica real de validação
|
||||||
|
//const bol = await GeradorService.ValidateCC(valor);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-md mx-auto p-6">
|
||||||
|
<GeradorCard
|
||||||
|
titulo="Cartão Cidadão"
|
||||||
|
onGerar={handleGenerateCC}
|
||||||
|
onValidar={handleValidateCC}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenerateCC;
|
||||||
|
|
||||||
45
geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx
Normal file
45
geradoresfe/src/components/Geradores/Tipos/GenerateNIF.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
import GeradorCard from "../GeradorCard";
|
||||||
|
import { EnumToOptions } from "../../../library/utils";
|
||||||
|
import { NIFType } from "../../../service/api";
|
||||||
|
import GeradorService from "../../../Api/GeradorApi";
|
||||||
|
|
||||||
|
const NifTypes = EnumToOptions(NIFType).map((opt) => ({
|
||||||
|
label: opt.label,
|
||||||
|
value: opt.value,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const GenerateNIF = () => {
|
||||||
|
|
||||||
|
const handleGenerateNIF = async (params: Record<string, string>) => {
|
||||||
|
const tipoSelecionado = params["Tipo de NIF"];
|
||||||
|
const nif = await GeradorService.GenerateNIF(tipoSelecionado);
|
||||||
|
|
||||||
|
return nif;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidateNIF = async (valor: string) => {
|
||||||
|
// Lógica real de validação
|
||||||
|
const bol = await GeradorService.ValidateNIF(valor);
|
||||||
|
return bol;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-md mx-auto p-6">
|
||||||
|
<GeradorCard
|
||||||
|
titulo="NIF"
|
||||||
|
parametros={[
|
||||||
|
{
|
||||||
|
nome: "Tipo de NIF",
|
||||||
|
tipo: "select",
|
||||||
|
options: NifTypes,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onGerar={handleGenerateNIF}
|
||||||
|
onValidar={handleValidateNIF}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenerateNIF;
|
||||||
29
geradoresfe/src/components/Geradores/Tipos/GenerateNISS.tsx
Normal file
29
geradoresfe/src/components/Geradores/Tipos/GenerateNISS.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import GeradorService from "../../../Api/GeradorApi";
|
||||||
|
import GeradorCard from "../GeradorCard.tsx";
|
||||||
|
|
||||||
|
const GenerateNISS = () => {
|
||||||
|
|
||||||
|
const handleGenerateNISS = async () => {
|
||||||
|
const niss = await GeradorService.GenerateNISS()
|
||||||
|
return niss;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleValidateNISS = async (valor: string) => {
|
||||||
|
// Lógica real de validação
|
||||||
|
const bol = await GeradorService.ValidateNISS(valor);
|
||||||
|
return bol;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-md mx-auto p-6">
|
||||||
|
<GeradorCard
|
||||||
|
titulo="NISS"
|
||||||
|
onGerar={handleGenerateNISS}
|
||||||
|
onValidar={handleValidateNISS}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GenerateNISS;
|
||||||
6
geradoresfe/src/components/Logo.tsx
Normal file
6
geradoresfe/src/components/Logo.tsx
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
// components/Logo.tsx
|
||||||
|
import logo from '../assets/Logotipo.png';
|
||||||
|
|
||||||
|
export const Logo = () => (
|
||||||
|
<img src={logo} alt="FactoryId logo" className="h-8 w-8" />
|
||||||
|
);
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import { useModal } from "../../hooks/useModal";
|
|
||||||
import { Modal } from "../ui/modal";
|
|
||||||
import Button from "../ui/button/Button";
|
|
||||||
import Input from "../form/input/InputField";
|
|
||||||
import Label from "../form/Label";
|
|
||||||
|
|
||||||
export default function UserAddressCard() {
|
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
|
||||||
const handleSave = () => {
|
|
||||||
// Handle save logic here
|
|
||||||
console.log("Saving changes...");
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="p-5 border border-gray-200 rounded-2xl dark:border-gray-800 lg:p-6">
|
|
||||||
<div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
|
||||||
<div>
|
|
||||||
<h4 className="text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Address
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-7 2xl:gap-x-32">
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Country
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
United States
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
City/State
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Phoenix, Arizona, United States
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Postal Code
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
ERT 2489
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
TAX ID
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
AS4568384
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={openModal}
|
|
||||||
className="flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Modal isOpen={isOpen} onClose={closeModal} className="max-w-[700px] m-4">
|
|
||||||
<div className="relative w-full p-4 overflow-y-auto bg-white no-scrollbar rounded-3xl dark:bg-gray-900 lg:p-11">
|
|
||||||
<div className="px-2 pr-14">
|
|
||||||
<h4 className="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Edit Address
|
|
||||||
</h4>
|
|
||||||
<p className="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
|
||||||
Update your details to keep your profile up-to-date.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<form className="flex flex-col">
|
|
||||||
<div className="px-2 overflow-y-auto custom-scrollbar">
|
|
||||||
<div className="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<Label>Country</Label>
|
|
||||||
<Input type="text" value="United Kingdom" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>City/State</Label>
|
|
||||||
<Input type="text" value="Leeds, East London" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>Postal Code</Label>
|
|
||||||
<Input type="text" value="ERT 2489" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>TAX ID</Label>
|
|
||||||
<Input type="text" value="AS4568384" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
|
||||||
<Button size="sm" variant="outline" onClick={closeModal}>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" onClick={handleSave}>
|
|
||||||
Save Changes
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useModal } from "../../hooks/useModal";
|
|
||||||
import { Modal } from "../ui/modal";
|
|
||||||
import Button from "../ui/button/Button";
|
|
||||||
import Input from "../form/input/InputField";
|
|
||||||
import Label from "../form/Label";
|
|
||||||
|
|
||||||
export default function UserInfoCard() {
|
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
|
||||||
const handleSave = () => {
|
|
||||||
// Handle save logic here
|
|
||||||
console.log("Saving changes...");
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="p-5 border border-gray-200 rounded-2xl dark:border-gray-800 lg:p-6">
|
|
||||||
<div className="flex flex-col gap-6 lg:flex-row lg:items-start lg:justify-between">
|
|
||||||
<div>
|
|
||||||
<h4 className="text-lg font-semibold text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Personal Information
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-4 lg:grid-cols-2 lg:gap-7 2xl:gap-x-32">
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
First Name
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Musharof
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Last Name
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Chowdhury
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Email address
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
randomuser@pimjo.com
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Phone
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
+09 363 398 46
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-2 text-xs leading-normal text-gray-500 dark:text-gray-400">
|
|
||||||
Bio
|
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Team Manager
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={openModal}
|
|
||||||
className="flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal isOpen={isOpen} onClose={closeModal} className="max-w-[700px] m-4">
|
|
||||||
<div className="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
|
||||||
<div className="px-2 pr-14">
|
|
||||||
<h4 className="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Edit Personal Information
|
|
||||||
</h4>
|
|
||||||
<p className="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
|
||||||
Update your details to keep your profile up-to-date.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<form className="flex flex-col">
|
|
||||||
<div className="custom-scrollbar h-[450px] overflow-y-auto px-2 pb-3">
|
|
||||||
<div>
|
|
||||||
<h5 className="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Social Links
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<Label>Facebook</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value="https://www.facebook.com/PimjoHQ"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>X.com</Label>
|
|
||||||
<Input type="text" value="https://x.com/PimjoHQ" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>Linkedin</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value="https://www.linkedin.com/company/pimjo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>Instagram</Label>
|
|
||||||
<Input type="text" value="https://instagram.com/PimjoHQ" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-7">
|
|
||||||
<h5 className="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Personal Information
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>First Name</Label>
|
|
||||||
<Input type="text" value="Emirhan" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Last Name</Label>
|
|
||||||
<Input type="text" value="Boruch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Email Address</Label>
|
|
||||||
<Input type="text" value="emirhanboruch55@gmail.com" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Phone</Label>
|
|
||||||
<Input type="text" value="+09 363 398 46" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2">
|
|
||||||
<Label>Bio</Label>
|
|
||||||
<Input type="text" value="Team Manager" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
|
||||||
<Button size="sm" variant="outline" onClick={closeModal}>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" onClick={handleSave}>
|
|
||||||
Save Changes
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,217 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useModal } from "../../hooks/useModal";
|
|
||||||
import { Modal } from "../ui/modal";
|
|
||||||
import Button from "../ui/button/Button";
|
|
||||||
import Input from "../form/input/InputField";
|
|
||||||
import Label from "../form/Label";
|
|
||||||
|
|
||||||
export default function UserMetaCard() {
|
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
|
||||||
const handleSave = () => {
|
|
||||||
// Handle save logic here
|
|
||||||
console.log("Saving changes...");
|
|
||||||
closeModal();
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="p-5 border border-gray-200 rounded-2xl dark:border-gray-800 lg:p-6">
|
|
||||||
<div className="flex flex-col gap-5 xl:flex-row xl:items-center xl:justify-between">
|
|
||||||
<div className="flex flex-col items-center w-full gap-6 xl:flex-row">
|
|
||||||
<div className="w-20 h-20 overflow-hidden border border-gray-200 rounded-full dark:border-gray-800">
|
|
||||||
<img src="/images/user/owner.jpg" alt="user" />
|
|
||||||
</div>
|
|
||||||
<div className="order-3 xl:order-2">
|
|
||||||
<h4 className="mb-2 text-lg font-semibold text-center text-gray-800 dark:text-white/90 xl:text-left">
|
|
||||||
Musharof Chowdhury
|
|
||||||
</h4>
|
|
||||||
<div className="flex flex-col items-center gap-1 text-center xl:flex-row xl:gap-3 xl:text-left">
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
Team Manager
|
|
||||||
</p>
|
|
||||||
<div className="hidden h-3.5 w-px bg-gray-300 dark:bg-gray-700 xl:block"></div>
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
Arizona, United States
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center order-2 gap-2 grow xl:order-3 xl:justify-end">
|
|
||||||
<button className="flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M11.6666 11.2503H13.7499L14.5833 7.91699H11.6666V6.25033C11.6666 5.39251 11.6666 4.58366 13.3333 4.58366H14.5833V1.78374C14.3118 1.7477 13.2858 1.66699 12.2023 1.66699C9.94025 1.66699 8.33325 3.04771 8.33325 5.58342V7.91699H5.83325V11.2503H8.33325V18.3337H11.6666V11.2503Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button className="flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M15.1708 1.875H17.9274L11.9049 8.75833L18.9899 18.125H13.4424L9.09742 12.4442L4.12578 18.125H1.36745L7.80912 10.7625L1.01245 1.875H6.70078L10.6283 7.0675L15.1708 1.875ZM14.2033 16.475H15.7308L5.87078 3.43833H4.23162L14.2033 16.475Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button className="flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M5.78381 4.16645C5.78351 4.84504 5.37181 5.45569 4.74286 5.71045C4.11391 5.96521 3.39331 5.81321 2.92083 5.32613C2.44836 4.83904 2.31837 4.11413 2.59216 3.49323C2.86596 2.87233 3.48886 2.47942 4.16715 2.49978C5.06804 2.52682 5.78422 3.26515 5.78381 4.16645ZM5.83381 7.06645H2.50048V17.4998H5.83381V7.06645ZM11.1005 7.06645H7.78381V17.4998H11.0672V12.0248C11.0672 8.97475 15.0422 8.69142 15.0422 12.0248V17.4998H18.3338V10.8914C18.3338 5.74978 12.4505 5.94145 11.0672 8.46642L11.1005 7.06645Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button className="flex h-11 w-11 items-center justify-center gap-2 rounded-full border border-gray-300 bg-white text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M10.8567 1.66699C11.7946 1.66854 12.2698 1.67351 12.6805 1.68573L12.8422 1.69102C13.0291 1.69766 13.2134 1.70599 13.4357 1.71641C14.3224 1.75738 14.9273 1.89766 15.4586 2.10391C16.0078 2.31572 16.4717 2.60183 16.9349 3.06503C17.3974 3.52822 17.6836 3.99349 17.8961 4.54141C18.1016 5.07197 18.2419 5.67753 18.2836 6.56433C18.2935 6.78655 18.3015 6.97088 18.3081 7.15775L18.3133 7.31949C18.3255 7.73011 18.3311 8.20543 18.3328 9.1433L18.3335 9.76463C18.3336 9.84055 18.3336 9.91888 18.3336 9.99972L18.3335 10.2348L18.333 10.8562C18.3314 11.794 18.3265 12.2694 18.3142 12.68L18.3089 12.8417C18.3023 13.0286 18.294 13.213 18.2836 13.4351C18.2426 14.322 18.1016 14.9268 17.8961 15.458C17.6842 16.0074 17.3974 16.4713 16.9349 16.9345C16.4717 17.397 16.0057 17.6831 15.4586 17.8955C14.9273 18.1011 14.3224 18.2414 13.4357 18.2831C13.2134 18.293 13.0291 18.3011 12.8422 18.3076L12.6805 18.3128C12.2698 18.3251 11.7946 18.3306 10.8567 18.3324L10.2353 18.333C10.1594 18.333 10.0811 18.333 10.0002 18.333H9.76516L9.14375 18.3325C8.20591 18.331 7.7306 18.326 7.31997 18.3137L7.15824 18.3085C6.97136 18.3018 6.78703 18.2935 6.56481 18.2831C5.67801 18.2421 5.07384 18.1011 4.5419 17.8955C3.99328 17.6838 3.5287 17.397 3.06551 16.9345C2.60231 16.4713 2.3169 16.0053 2.1044 15.458C1.89815 14.9268 1.75856 14.322 1.7169 13.4351C1.707 13.213 1.69892 13.0286 1.69238 12.8417L1.68714 12.68C1.67495 12.2694 1.66939 11.794 1.66759 10.8562L1.66748 9.1433C1.66903 8.20543 1.67399 7.73011 1.68621 7.31949L1.69151 7.15775C1.69815 6.97088 1.70648 6.78655 1.7169 6.56433C1.75786 5.67683 1.89815 5.07266 2.1044 4.54141C2.3162 3.9928 2.60231 3.52822 3.06551 3.06503C3.5287 2.60183 3.99398 2.31641 4.5419 2.10391C5.07315 1.89766 5.67731 1.75808 6.56481 1.71641C6.78703 1.70652 6.97136 1.69844 7.15824 1.6919L7.31997 1.68666C7.7306 1.67446 8.20591 1.6689 9.14375 1.6671L10.8567 1.66699ZM10.0002 5.83308C7.69781 5.83308 5.83356 7.69935 5.83356 9.99972C5.83356 12.3021 7.69984 14.1664 10.0002 14.1664C12.3027 14.1664 14.1669 12.3001 14.1669 9.99972C14.1669 7.69732 12.3006 5.83308 10.0002 5.83308ZM10.0002 7.49974C11.381 7.49974 12.5002 8.61863 12.5002 9.99972C12.5002 11.3805 11.3813 12.4997 10.0002 12.4997C8.6195 12.4997 7.50023 11.3809 7.50023 9.99972C7.50023 8.61897 8.61908 7.49974 10.0002 7.49974ZM14.3752 4.58308C13.8008 4.58308 13.3336 5.04967 13.3336 5.62403C13.3336 6.19841 13.8002 6.66572 14.3752 6.66572C14.9496 6.66572 15.4169 6.19913 15.4169 5.62403C15.4169 5.04967 14.9488 4.58236 14.3752 4.58308Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
onClick={openModal}
|
|
||||||
className="flex w-full items-center justify-center gap-2 rounded-full border border-gray-300 bg-white px-4 py-3 text-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200 lg:inline-flex lg:w-auto"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="18"
|
|
||||||
height="18"
|
|
||||||
viewBox="0 0 18 18"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M15.0911 2.78206C14.2125 1.90338 12.7878 1.90338 11.9092 2.78206L4.57524 10.116C4.26682 10.4244 4.0547 10.8158 3.96468 11.2426L3.31231 14.3352C3.25997 14.5833 3.33653 14.841 3.51583 15.0203C3.69512 15.1996 3.95286 15.2761 4.20096 15.2238L7.29355 14.5714C7.72031 14.4814 8.11172 14.2693 8.42013 13.9609L15.7541 6.62695C16.6327 5.74827 16.6327 4.32365 15.7541 3.44497L15.0911 2.78206ZM12.9698 3.84272C13.2627 3.54982 13.7376 3.54982 14.0305 3.84272L14.6934 4.50563C14.9863 4.79852 14.9863 5.2734 14.6934 5.56629L14.044 6.21573L12.3204 4.49215L12.9698 3.84272ZM11.2597 5.55281L5.6359 11.1766C5.53309 11.2794 5.46238 11.4099 5.43238 11.5522L5.01758 13.5185L6.98394 13.1037C7.1262 13.0737 7.25666 13.003 7.35947 12.9002L12.9833 7.27639L11.2597 5.55281Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Modal isOpen={isOpen} onClose={closeModal} className="max-w-[700px] m-4">
|
|
||||||
<div className="no-scrollbar relative w-full max-w-[700px] overflow-y-auto rounded-3xl bg-white p-4 dark:bg-gray-900 lg:p-11">
|
|
||||||
<div className="px-2 pr-14">
|
|
||||||
<h4 className="mb-2 text-2xl font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Edit Personal Information
|
|
||||||
</h4>
|
|
||||||
<p className="mb-6 text-sm text-gray-500 dark:text-gray-400 lg:mb-7">
|
|
||||||
Update your details to keep your profile up-to-date.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<form className="flex flex-col">
|
|
||||||
<div className="custom-scrollbar h-[450px] pb-3 overflow-y-auto px-2">
|
|
||||||
<div>
|
|
||||||
<h5 className="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Social Links
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<Label>Facebook</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value="https://www.facebook.com/PimjoHQ"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>X.com</Label>
|
|
||||||
<Input type="text" value="https://x.com/PimjoHQ" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>Linkedin</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value="https://www.linkedin.com/company/pimjo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<Label>Instagram</Label>
|
|
||||||
<Input type="text" value="https://instagram.com/PimjoHQ" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="mt-7">
|
|
||||||
<h5 className="mb-5 text-lg font-medium text-gray-800 dark:text-white/90 lg:mb-6">
|
|
||||||
Personal Information
|
|
||||||
</h5>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 gap-x-6 gap-y-5 lg:grid-cols-2">
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>First Name</Label>
|
|
||||||
<Input type="text" value="Musharof" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Last Name</Label>
|
|
||||||
<Input type="text" value="Chowdhury" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Email Address</Label>
|
|
||||||
<Input type="text" value="randomuser@pimjo.com" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2 lg:col-span-1">
|
|
||||||
<Label>Phone</Label>
|
|
||||||
<Input type="text" value="+09 363 398 46" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-span-2">
|
|
||||||
<Label>Bio</Label>
|
|
||||||
<Input type="text" value="Team Manager" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3 px-2 mt-6 lg:justify-end">
|
|
||||||
<Button size="sm" variant="outline" onClick={closeModal}>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
<Button size="sm" onClick={handleSave}>
|
|
||||||
Save Changes
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Chart from "react-apexcharts";
|
|
||||||
import { ApexOptions } from "apexcharts";
|
|
||||||
|
|
||||||
export default function BarChartOne() {
|
|
||||||
const options: ApexOptions = {
|
|
||||||
colors: ["#465fff"],
|
|
||||||
chart: {
|
|
||||||
fontFamily: "Outfit, sans-serif",
|
|
||||||
type: "bar",
|
|
||||||
height: 180,
|
|
||||||
toolbar: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
horizontal: false,
|
|
||||||
columnWidth: "39%",
|
|
||||||
borderRadius: 5,
|
|
||||||
borderRadiusApplication: "end",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
stroke: {
|
|
||||||
show: true,
|
|
||||||
width: 4,
|
|
||||||
colors: ["transparent"],
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
categories: [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
],
|
|
||||||
axisBorder: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
show: true,
|
|
||||||
position: "top",
|
|
||||||
horizontalAlign: "left",
|
|
||||||
fontFamily: "Outfit",
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
title: {
|
|
||||||
text: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
yaxis: {
|
|
||||||
lines: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fill: {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip: {
|
|
||||||
x: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
formatter: (val: number) => `${val}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const series = [
|
|
||||||
{
|
|
||||||
name: "Sales",
|
|
||||||
data: [168, 385, 201, 298, 187, 195, 291, 110, 215, 390, 280, 112],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<div className="max-w-full overflow-x-auto custom-scrollbar">
|
|
||||||
<div id="chartOne" className="min-w-[1000px]">
|
|
||||||
<Chart options={options} series={series} type="bar" height={180} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Chart from "react-apexcharts";
|
|
||||||
import { ApexOptions } from "apexcharts";
|
|
||||||
|
|
||||||
export default function LineChartOne() {
|
|
||||||
const options: ApexOptions = {
|
|
||||||
legend: {
|
|
||||||
show: false, // Hide legend
|
|
||||||
position: "top",
|
|
||||||
horizontalAlign: "left",
|
|
||||||
},
|
|
||||||
colors: ["#465FFF", "#9CB9FF"], // Define line colors
|
|
||||||
chart: {
|
|
||||||
fontFamily: "Outfit, sans-serif",
|
|
||||||
height: 310,
|
|
||||||
type: "line", // Set the chart type to 'line'
|
|
||||||
toolbar: {
|
|
||||||
show: false, // Hide chart toolbar
|
|
||||||
},
|
|
||||||
},
|
|
||||||
stroke: {
|
|
||||||
curve: "straight", // Define the line style (straight, smooth, or step)
|
|
||||||
width: [2, 2], // Line width for each dataset
|
|
||||||
},
|
|
||||||
|
|
||||||
fill: {
|
|
||||||
type: "gradient",
|
|
||||||
gradient: {
|
|
||||||
opacityFrom: 0.55,
|
|
||||||
opacityTo: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
markers: {
|
|
||||||
size: 0, // Size of the marker points
|
|
||||||
strokeColors: "#fff", // Marker border color
|
|
||||||
strokeWidth: 2,
|
|
||||||
hover: {
|
|
||||||
size: 6, // Marker size on hover
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
xaxis: {
|
|
||||||
lines: {
|
|
||||||
show: false, // Hide grid lines on x-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
lines: {
|
|
||||||
show: true, // Show grid lines on y-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
enabled: false, // Disable data labels
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: true, // Enable tooltip
|
|
||||||
x: {
|
|
||||||
format: "dd MMM yyyy", // Format for x-axis tooltip
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
type: "category", // Category-based x-axis
|
|
||||||
categories: [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
],
|
|
||||||
axisBorder: {
|
|
||||||
show: false, // Hide x-axis border
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false, // Hide x-axis ticks
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: false, // Disable tooltip for x-axis points
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
fontSize: "12px", // Adjust font size for y-axis labels
|
|
||||||
colors: ["#6B7280"], // Color of the labels
|
|
||||||
},
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: "", // Remove y-axis title
|
|
||||||
style: {
|
|
||||||
fontSize: "0px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const series = [
|
|
||||||
{
|
|
||||||
name: "Sales",
|
|
||||||
data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Revenue",
|
|
||||||
data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<div className="max-w-full overflow-x-auto custom-scrollbar">
|
|
||||||
<div id="chartEight" className="min-w-[1000px]">
|
|
||||||
<Chart options={options} series={series} type="area" height={310} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
const ChartTab: React.FC = () => {
|
|
||||||
const [selected, setSelected] = useState<
|
|
||||||
"optionOne" | "optionTwo" | "optionThree"
|
|
||||||
>("optionOne");
|
|
||||||
|
|
||||||
const getButtonClass = (option: "optionOne" | "optionTwo" | "optionThree") =>
|
|
||||||
selected === option
|
|
||||||
? "shadow-theme-xs text-gray-900 dark:text-white bg-white dark:bg-gray-800"
|
|
||||||
: "text-gray-500 dark:text-gray-400";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-0.5 rounded-lg bg-gray-100 p-0.5 dark:bg-gray-900">
|
|
||||||
<button
|
|
||||||
onClick={() => setSelected("optionOne")}
|
|
||||||
className={`px-3 py-2 font-medium w-full rounded-md text-theme-sm hover:text-gray-900 dark:hover:text-white ${getButtonClass(
|
|
||||||
"optionOne"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
Monthly
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => setSelected("optionTwo")}
|
|
||||||
className={`px-3 py-2 font-medium w-full rounded-md text-theme-sm hover:text-gray-900 dark:hover:text-white ${getButtonClass(
|
|
||||||
"optionTwo"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
Quarterly
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={() => setSelected("optionThree")}
|
|
||||||
className={`px-3 py-2 font-medium w-full rounded-md text-theme-sm hover:text-gray-900 dark:hover:text-white ${getButtonClass(
|
|
||||||
"optionThree"
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
Annually
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ChartTab;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface ComponentCardProps {
|
|
||||||
title?: string; // O título agora é opcional
|
|
||||||
children: React.ReactNode;
|
|
||||||
className?: string; // Additional custom classes for styling
|
|
||||||
desc?: string; // Description text
|
|
||||||
}
|
|
||||||
|
|
||||||
const ComponentCard: React.FC<ComponentCardProps> = ({
|
|
||||||
title,
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
desc = "",
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`rounded-2xl border border-gray-200 bg-white dark:border-gray-800 dark:bg-white/[0.03] ${className}`}
|
|
||||||
>
|
|
||||||
{/* Card Header - Só renderiza se o título for passado */}
|
|
||||||
{title && (
|
|
||||||
<div className="px-6 py-5">
|
|
||||||
<h3 className="text-base font-medium text-gray-800 dark:text-white/90">
|
|
||||||
{title}
|
|
||||||
</h3>
|
|
||||||
{desc && (
|
|
||||||
<p className="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
{desc}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Card Body */}
|
|
||||||
<div className="p-4 border-t border-gray-100 dark:border-gray-800 sm:p-6">
|
|
||||||
<div className="space-y-6">{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ComponentCard;
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
|
||||||
|
|
||||||
interface CountdownTimerProps {
|
|
||||||
targetDate: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CountdownTimer: React.FC<CountdownTimerProps> = ({ targetDate }) => {
|
|
||||||
const [timeLeft, setTimeLeft] = useState({
|
|
||||||
days: 0,
|
|
||||||
hours: 0,
|
|
||||||
minutes: 0,
|
|
||||||
seconds: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const timer = setInterval(() => {
|
|
||||||
const now = new Date();
|
|
||||||
const difference = targetDate.getTime() - now.getTime();
|
|
||||||
|
|
||||||
if (difference > 0) {
|
|
||||||
const days = Math.floor(difference / (1000 * 60 * 60 * 24));
|
|
||||||
const hours = Math.floor((difference / (1000 * 60 * 60)) % 24);
|
|
||||||
const minutes = Math.floor((difference / 1000 / 60) % 60);
|
|
||||||
const seconds = Math.floor((difference / 1000) % 60);
|
|
||||||
|
|
||||||
setTimeLeft({ days, hours, minutes, seconds });
|
|
||||||
} else {
|
|
||||||
clearInterval(timer);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
return () => clearInterval(timer);
|
|
||||||
}, [targetDate]);
|
|
||||||
|
|
||||||
const formatTime = (time: number): string => {
|
|
||||||
return time.toString().padStart(2, "0");
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mb-10">
|
|
||||||
<div className="flex flex-wrap justify-center gap-2 mb-2 font-bold text-title-md text-brand-500 dark:text-brand-400 xl:text-title-lg">
|
|
||||||
<div className="timer-box">
|
|
||||||
<span>{formatTime(timeLeft.days)}</span>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div className="timer-box">
|
|
||||||
<span>{formatTime(timeLeft.hours)}</span>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div className="timer-box">
|
|
||||||
<span>{formatTime(timeLeft.minutes)}</span>
|
|
||||||
</div>
|
|
||||||
:
|
|
||||||
<div className="timer-box">
|
|
||||||
<span>{formatTime(timeLeft.seconds)}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-base text-center text-gray-500 dark:text-gray-400">
|
|
||||||
<span className="inline-block timer-box">
|
|
||||||
<span className="inline-block">{timeLeft.days}</span>
|
|
||||||
</span>
|
|
||||||
{timeLeft.days === 1 ? " day" : " days"} left
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CountdownTimer;
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function GridShape() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="absolute right-0 top-0 -z-1 w-full max-w-[250px] xl:max-w-[450px]">
|
|
||||||
<img src="/images/shape/grid-01.svg" alt="grid" />
|
|
||||||
</div>
|
|
||||||
<div className="absolute bottom-0 left-0 -z-1 w-full max-w-[250px] rotate-180 xl:max-w-[450px]">
|
|
||||||
<img src="/images/shape/grid-01.svg" alt="grid" />
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
|
|
||||||
interface BreadcrumbProps {
|
|
||||||
pageTitle: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const PageBreadcrumb: React.FC<BreadcrumbProps> = ({ pageTitle }) => {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-wrap items-center justify-between gap-3 mb-6">
|
|
||||||
<h2
|
|
||||||
className="text-xl font-semibold text-gray-800 dark:text-white/90"
|
|
||||||
x-text="pageName"
|
|
||||||
>
|
|
||||||
{pageTitle}
|
|
||||||
</h2>
|
|
||||||
<nav>
|
|
||||||
<ol className="flex items-center gap-1.5">
|
|
||||||
<li>
|
|
||||||
<Link
|
|
||||||
className="inline-flex items-center gap-1.5 text-sm text-gray-500 dark:text-gray-400"
|
|
||||||
to="/"
|
|
||||||
>
|
|
||||||
Home
|
|
||||||
<svg
|
|
||||||
className="stroke-current"
|
|
||||||
width="17"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 17 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M6.0765 12.667L10.2432 8.50033L6.0765 4.33366"
|
|
||||||
stroke=""
|
|
||||||
strokeWidth="1.2"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li className="text-sm text-gray-800 dark:text-white/90">
|
|
||||||
{pageTitle}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PageBreadcrumb;
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { HelmetProvider, Helmet } from "react-helmet-async";
|
|
||||||
|
|
||||||
const PageMeta = ({
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
}: {
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
}) => (
|
|
||||||
<Helmet>
|
|
||||||
<title>{title}</title>
|
|
||||||
<meta name="description" content={description} />
|
|
||||||
</Helmet>
|
|
||||||
);
|
|
||||||
|
|
||||||
export const AppWrapper = ({ children }: { children: React.ReactNode }) => (
|
|
||||||
<HelmetProvider>{children}</HelmetProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default PageMeta;
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { useTheme } from "../../context/ThemeContext";
|
|
||||||
|
|
||||||
export const ThemeToggleButton: React.FC = () => {
|
|
||||||
const { toggleTheme } = useTheme();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={toggleTheme}
|
|
||||||
className="relative flex items-center justify-center text-gray-500 transition-colors bg-white border border-gray-200 rounded-full hover:text-dark-900 h-11 w-11 hover:bg-gray-100 hover:text-gray-700 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="hidden dark:block"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M9.99998 1.5415C10.4142 1.5415 10.75 1.87729 10.75 2.2915V3.5415C10.75 3.95572 10.4142 4.2915 9.99998 4.2915C9.58577 4.2915 9.24998 3.95572 9.24998 3.5415V2.2915C9.24998 1.87729 9.58577 1.5415 9.99998 1.5415ZM10.0009 6.79327C8.22978 6.79327 6.79402 8.22904 6.79402 10.0001C6.79402 11.7712 8.22978 13.207 10.0009 13.207C11.772 13.207 13.2078 11.7712 13.2078 10.0001C13.2078 8.22904 11.772 6.79327 10.0009 6.79327ZM5.29402 10.0001C5.29402 7.40061 7.40135 5.29327 10.0009 5.29327C12.6004 5.29327 14.7078 7.40061 14.7078 10.0001C14.7078 12.5997 12.6004 14.707 10.0009 14.707C7.40135 14.707 5.29402 12.5997 5.29402 10.0001ZM15.9813 5.08035C16.2742 4.78746 16.2742 4.31258 15.9813 4.01969C15.6884 3.7268 15.2135 3.7268 14.9207 4.01969L14.0368 4.90357C13.7439 5.19647 13.7439 5.67134 14.0368 5.96423C14.3297 6.25713 14.8045 6.25713 15.0974 5.96423L15.9813 5.08035ZM18.4577 10.0001C18.4577 10.4143 18.1219 10.7501 17.7077 10.7501H16.4577C16.0435 10.7501 15.7077 10.4143 15.7077 10.0001C15.7077 9.58592 16.0435 9.25013 16.4577 9.25013H17.7077C18.1219 9.25013 18.4577 9.58592 18.4577 10.0001ZM14.9207 15.9806C15.2135 16.2735 15.6884 16.2735 15.9813 15.9806C16.2742 15.6877 16.2742 15.2128 15.9813 14.9199L15.0974 14.036C14.8045 13.7431 14.3297 13.7431 14.0368 14.036C13.7439 14.3289 13.7439 14.8038 14.0368 15.0967L14.9207 15.9806ZM9.99998 15.7088C10.4142 15.7088 10.75 16.0445 10.75 16.4588V17.7088C10.75 18.123 10.4142 18.4588 9.99998 18.4588C9.58577 18.4588 9.24998 18.123 9.24998 17.7088V16.4588C9.24998 16.0445 9.58577 15.7088 9.99998 15.7088ZM5.96356 15.0972C6.25646 14.8043 6.25646 14.3295 5.96356 14.0366C5.67067 13.7437 5.1958 13.7437 4.9029 14.0366L4.01902 14.9204C3.72613 15.2133 3.72613 15.6882 4.01902 15.9811C4.31191 16.274 4.78679 16.274 5.07968 15.9811L5.96356 15.0972ZM4.29224 10.0001C4.29224 10.4143 3.95645 10.7501 3.54224 10.7501H2.29224C1.87802 10.7501 1.54224 10.4143 1.54224 10.0001C1.54224 9.58592 1.87802 9.25013 2.29224 9.25013H3.54224C3.95645 9.25013 4.29224 9.58592 4.29224 10.0001ZM4.9029 5.9637C5.1958 6.25659 5.67067 6.25659 5.96356 5.9637C6.25646 5.6708 6.25646 5.19593 5.96356 4.90303L5.07968 4.01915C4.78679 3.72626 4.31191 3.72626 4.01902 4.01915C3.72613 4.31204 3.72613 4.78692 4.01902 5.07981L4.9029 5.9637Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
className="dark:hidden"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M17.4547 11.97L18.1799 12.1611C18.265 11.8383 18.1265 11.4982 17.8401 11.3266C17.5538 11.1551 17.1885 11.1934 16.944 11.4207L17.4547 11.97ZM8.0306 2.5459L8.57989 3.05657C8.80718 2.81209 8.84554 2.44682 8.67398 2.16046C8.50243 1.8741 8.16227 1.73559 7.83948 1.82066L8.0306 2.5459ZM12.9154 13.0035C9.64678 13.0035 6.99707 10.3538 6.99707 7.08524H5.49707C5.49707 11.1823 8.81835 14.5035 12.9154 14.5035V13.0035ZM16.944 11.4207C15.8869 12.4035 14.4721 13.0035 12.9154 13.0035V14.5035C14.8657 14.5035 16.6418 13.7499 17.9654 12.5193L16.944 11.4207ZM16.7295 11.7789C15.9437 14.7607 13.2277 16.9586 10.0003 16.9586V18.4586C13.9257 18.4586 17.2249 15.7853 18.1799 12.1611L16.7295 11.7789ZM10.0003 16.9586C6.15734 16.9586 3.04199 13.8433 3.04199 10.0003H1.54199C1.54199 14.6717 5.32892 18.4586 10.0003 18.4586V16.9586ZM3.04199 10.0003C3.04199 6.77289 5.23988 4.05695 8.22173 3.27114L7.83948 1.82066C4.21532 2.77574 1.54199 6.07486 1.54199 10.0003H3.04199ZM6.99707 7.08524C6.99707 5.52854 7.5971 4.11366 8.57989 3.05657L7.48132 2.03522C6.25073 3.35885 5.49707 5.13487 5.49707 7.08524H6.99707Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
// react plugin for creating vector maps
|
|
||||||
import { VectorMap } from "@react-jvectormap/core";
|
|
||||||
import { worldMill } from "@react-jvectormap/world";
|
|
||||||
|
|
||||||
// Define the component props
|
|
||||||
interface CountryMapProps {
|
|
||||||
mapColor?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CountryMap: React.FC<CountryMapProps> = ({ mapColor }) => {
|
|
||||||
return (
|
|
||||||
<VectorMap
|
|
||||||
map={worldMill}
|
|
||||||
backgroundColor="transparent"
|
|
||||||
markerStyle={{
|
|
||||||
initial: {
|
|
||||||
fill: "#465FFF",
|
|
||||||
r: 4, // Custom radius for markers
|
|
||||||
} as any, // Type assertion to bypass strict CSS property checks
|
|
||||||
}}
|
|
||||||
markersSelectable={true}
|
|
||||||
markers={[
|
|
||||||
{
|
|
||||||
latLng: [37.2580397, -104.657039],
|
|
||||||
name: "United States",
|
|
||||||
style: {
|
|
||||||
fill: "#465FFF",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "white",
|
|
||||||
stroke: "#383f47",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
latLng: [20.7504374, 73.7276105],
|
|
||||||
name: "India",
|
|
||||||
style: { fill: "#465FFF", borderWidth: 1, borderColor: "white" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
latLng: [53.613, -11.6368],
|
|
||||||
name: "United Kingdom",
|
|
||||||
style: { fill: "#465FFF", borderWidth: 1, borderColor: "white" },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
latLng: [-25.0304388, 115.2092761],
|
|
||||||
name: "Sweden",
|
|
||||||
style: {
|
|
||||||
fill: "#465FFF",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "white",
|
|
||||||
strokeOpacity: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
zoomOnScroll={false}
|
|
||||||
zoomMax={12}
|
|
||||||
zoomMin={1}
|
|
||||||
zoomAnimate={true}
|
|
||||||
zoomStep={1.5}
|
|
||||||
regionStyle={{
|
|
||||||
initial: {
|
|
||||||
fill: mapColor || "#D0D5DD",
|
|
||||||
fillOpacity: 1,
|
|
||||||
fontFamily: "Outfit",
|
|
||||||
stroke: "none",
|
|
||||||
strokeWidth: 0,
|
|
||||||
strokeOpacity: 0,
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
fillOpacity: 0.7,
|
|
||||||
cursor: "pointer",
|
|
||||||
fill: "#465fff",
|
|
||||||
stroke: "none",
|
|
||||||
},
|
|
||||||
selected: {
|
|
||||||
fill: "#465FFF",
|
|
||||||
},
|
|
||||||
selectedHover: {},
|
|
||||||
}}
|
|
||||||
regionLabelStyle={{
|
|
||||||
initial: {
|
|
||||||
fill: "#35373e",
|
|
||||||
fontWeight: 500,
|
|
||||||
fontSize: "13px",
|
|
||||||
stroke: "none",
|
|
||||||
},
|
|
||||||
hover: {},
|
|
||||||
selected: {},
|
|
||||||
selectedHover: {},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CountryMap;
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
|
||||||
import CountryMap from "./CountryMap";
|
|
||||||
import { MoreDotIcon } from "../../icons";
|
|
||||||
|
|
||||||
export default function DemographicCard() {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
function toggleDropdown() {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDropdown() {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] sm:p-6">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Customers Demographic
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
Number of customer based on country
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative inline-block">
|
|
||||||
<button onClick={toggleDropdown}>
|
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300" />
|
|
||||||
</button>
|
|
||||||
<Dropdown
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={closeDropdown}
|
|
||||||
className="w-40 p-2"
|
|
||||||
>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
View More
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownItem>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="px-4 py-6 my-6 overflow-hidden border border-gary-200 rounded-2xl dark:border-gray-800 sm:px-6">
|
|
||||||
<div
|
|
||||||
id="mapOne"
|
|
||||||
className="mapOne map-btn -mx-4 -my-6 h-[212px] w-[252px] 2xsm:w-[307px] xsm:w-[358px] sm:-mx-6 md:w-[668px] lg:w-[634px] xl:w-[393px] 2xl:w-[554px]"
|
|
||||||
>
|
|
||||||
<CountryMap />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-5">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="items-center w-full rounded-full max-w-8">
|
|
||||||
<img src="./images/country/country-01.svg" alt="usa" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-semibold text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
USA
|
|
||||||
</p>
|
|
||||||
<span className="block text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
2,379 Customers
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex w-full max-w-[140px] items-center gap-3">
|
|
||||||
<div className="relative block h-2 w-full max-w-[100px] rounded bg-gray-200 dark:bg-gray-800">
|
|
||||||
<div className="absolute left-0 top-0 flex h-full w-[79%] items-center justify-center rounded bg-brand-500 text-xs font-medium text-white"></div>
|
|
||||||
</div>
|
|
||||||
<p className="font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
79%
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="items-center w-full rounded-full max-w-8">
|
|
||||||
<img src="./images/country/country-02.svg" alt="france" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-semibold text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
France
|
|
||||||
</p>
|
|
||||||
<span className="block text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
589 Customers
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex w-full max-w-[140px] items-center gap-3">
|
|
||||||
<div className="relative block h-2 w-full max-w-[100px] rounded bg-gray-200 dark:bg-gray-800">
|
|
||||||
<div className="absolute left-0 top-0 flex h-full w-[23%] items-center justify-center rounded bg-brand-500 text-xs font-medium text-white"></div>
|
|
||||||
</div>
|
|
||||||
<p className="font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
23%
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
ArrowDownIcon,
|
|
||||||
ArrowUpIcon,
|
|
||||||
BoxIconLine,
|
|
||||||
GroupIcon,
|
|
||||||
} from "../../icons";
|
|
||||||
import Badge from "../ui/badge/Badge";
|
|
||||||
|
|
||||||
export default function EcommerceMetrics() {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-6">
|
|
||||||
{/* <!-- Metric Item Start --> */}
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6">
|
|
||||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
|
||||||
<GroupIcon className="text-gray-800 size-6 dark:text-white/90" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-end justify-between mt-5">
|
|
||||||
<div>
|
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
Customers
|
|
||||||
</span>
|
|
||||||
<h4 className="mt-2 font-bold text-gray-800 text-title-sm dark:text-white/90">
|
|
||||||
3,782
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
<Badge color="success">
|
|
||||||
<ArrowUpIcon />
|
|
||||||
11.01%
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* <!-- Metric Item End --> */}
|
|
||||||
|
|
||||||
{/* <!-- Metric Item Start --> */}
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white p-5 dark:border-gray-800 dark:bg-white/[0.03] md:p-6">
|
|
||||||
<div className="flex items-center justify-center w-12 h-12 bg-gray-100 rounded-xl dark:bg-gray-800">
|
|
||||||
<BoxIconLine className="text-gray-800 dark:text-white/90" />
|
|
||||||
</div>
|
|
||||||
<div className="flex items-end justify-between mt-5">
|
|
||||||
<div>
|
|
||||||
<span className="text-sm text-gray-500 dark:text-gray-400">
|
|
||||||
Orders
|
|
||||||
</span>
|
|
||||||
<h4 className="mt-2 font-bold text-gray-800 text-title-sm dark:text-white/90">
|
|
||||||
5,359
|
|
||||||
</h4>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Badge color="error">
|
|
||||||
<ArrowDownIcon />
|
|
||||||
9.05%
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/* <!-- Metric Item End --> */}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
import Chart from "react-apexcharts";
|
|
||||||
import { ApexOptions } from "apexcharts";
|
|
||||||
import { MoreDotIcon } from "../../icons";
|
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
export default function MonthlySalesChart() {
|
|
||||||
const options: ApexOptions = {
|
|
||||||
colors: ["#465fff"],
|
|
||||||
chart: {
|
|
||||||
fontFamily: "Outfit, sans-serif",
|
|
||||||
type: "bar",
|
|
||||||
height: 180,
|
|
||||||
toolbar: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
bar: {
|
|
||||||
horizontal: false,
|
|
||||||
columnWidth: "39%",
|
|
||||||
borderRadius: 5,
|
|
||||||
borderRadiusApplication: "end",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
stroke: {
|
|
||||||
show: true,
|
|
||||||
width: 4,
|
|
||||||
colors: ["transparent"],
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
categories: [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
],
|
|
||||||
axisBorder: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
show: true,
|
|
||||||
position: "top",
|
|
||||||
horizontalAlign: "left",
|
|
||||||
fontFamily: "Outfit",
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
title: {
|
|
||||||
text: undefined,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
yaxis: {
|
|
||||||
lines: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fill: {
|
|
||||||
opacity: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
tooltip: {
|
|
||||||
x: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
formatter: (val: number) => `${val}`,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const series = [
|
|
||||||
{
|
|
||||||
name: "Sales",
|
|
||||||
data: [168, 385, 201, 298, 187, 195, 291, 110, 215, 390, 280, 112],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
function toggleDropdown() {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDropdown() {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Monthly Sales
|
|
||||||
</h3>
|
|
||||||
<div className="relative inline-block">
|
|
||||||
<button onClick={toggleDropdown}>
|
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300" />
|
|
||||||
</button>
|
|
||||||
<Dropdown
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={closeDropdown}
|
|
||||||
className="w-40 p-2"
|
|
||||||
>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
View More
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownItem>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-full overflow-x-auto custom-scrollbar">
|
|
||||||
<div className="-ml-5 min-w-[650px] xl:min-w-full pl-2">
|
|
||||||
<Chart options={options} series={series} type="bar" height={180} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
import Chart from "react-apexcharts";
|
|
||||||
import { ApexOptions } from "apexcharts";
|
|
||||||
import { useState } from "react";
|
|
||||||
import { MoreDotIcon } from "../../icons";
|
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
|
||||||
|
|
||||||
export default function MonthlyTarget() {
|
|
||||||
const series = [75.55];
|
|
||||||
const options: ApexOptions = {
|
|
||||||
colors: ["#465FFF"],
|
|
||||||
chart: {
|
|
||||||
fontFamily: "Outfit, sans-serif",
|
|
||||||
type: "radialBar",
|
|
||||||
height: 330,
|
|
||||||
sparkline: {
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plotOptions: {
|
|
||||||
radialBar: {
|
|
||||||
startAngle: -85,
|
|
||||||
endAngle: 85,
|
|
||||||
hollow: {
|
|
||||||
size: "80%",
|
|
||||||
},
|
|
||||||
track: {
|
|
||||||
background: "#E4E7EC",
|
|
||||||
strokeWidth: "100%",
|
|
||||||
margin: 5, // margin is in pixels
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
name: {
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
fontSize: "36px",
|
|
||||||
fontWeight: "600",
|
|
||||||
offsetY: -40,
|
|
||||||
color: "#1D2939",
|
|
||||||
formatter: function (val) {
|
|
||||||
return val + "%";
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
fill: {
|
|
||||||
type: "solid",
|
|
||||||
colors: ["#465FFF"],
|
|
||||||
},
|
|
||||||
stroke: {
|
|
||||||
lineCap: "round",
|
|
||||||
},
|
|
||||||
labels: ["Progress"],
|
|
||||||
};
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
function toggleDropdown() {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDropdown() {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-gray-100 dark:border-gray-800 dark:bg-white/[0.03]">
|
|
||||||
<div className="px-5 pt-5 bg-white shadow-default rounded-2xl pb-11 dark:bg-gray-900 sm:px-6 sm:pt-6">
|
|
||||||
<div className="flex justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Monthly Target
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
Target you’ve set for each month
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="relative inline-block">
|
|
||||||
<button onClick={toggleDropdown}>
|
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300" />
|
|
||||||
</button>
|
|
||||||
<Dropdown
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={closeDropdown}
|
|
||||||
className="w-40 p-2"
|
|
||||||
>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
View More
|
|
||||||
</DropdownItem>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex w-full font-normal text-left text-gray-500 rounded-lg hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
Delete
|
|
||||||
</DropdownItem>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="relative ">
|
|
||||||
<div className="max-h-[330px]">
|
|
||||||
<Chart
|
|
||||||
options={options}
|
|
||||||
series={series}
|
|
||||||
type="radialBar"
|
|
||||||
height={330}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className="absolute left-1/2 top-full -translate-x-1/2 -translate-y-[95%] rounded-full bg-success-50 px-3 py-1 text-xs font-medium text-success-600 dark:bg-success-500/15 dark:text-success-500">
|
|
||||||
+10%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<p className="mx-auto mt-10 w-full max-w-[380px] text-center text-sm text-gray-500 sm:text-base">
|
|
||||||
You earn $3287 today, it's higher than last month. Keep up your good
|
|
||||||
work!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-center gap-5 px-6 py-3.5 sm:gap-8 sm:py-5">
|
|
||||||
<div>
|
|
||||||
<p className="mb-1 text-center text-gray-500 text-theme-xs dark:text-gray-400 sm:text-sm">
|
|
||||||
Target
|
|
||||||
</p>
|
|
||||||
<p className="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
|
||||||
$20K
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M7.26816 13.6632C7.4056 13.8192 7.60686 13.9176 7.8311 13.9176C7.83148 13.9176 7.83187 13.9176 7.83226 13.9176C8.02445 13.9178 8.21671 13.8447 8.36339 13.6981L12.3635 9.70076C12.6565 9.40797 12.6567 8.9331 12.3639 8.6401C12.0711 8.34711 11.5962 8.34694 11.3032 8.63973L8.5811 11.36L8.5811 2.5C8.5811 2.08579 8.24531 1.75 7.8311 1.75C7.41688 1.75 7.0811 2.08579 7.0811 2.5L7.0811 11.3556L4.36354 8.63975C4.07055 8.34695 3.59568 8.3471 3.30288 8.64009C3.01008 8.93307 3.01023 9.40794 3.30321 9.70075L7.26816 13.6632Z"
|
|
||||||
fill="#D92D20"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-px bg-gray-200 h-7 dark:bg-gray-800"></div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-1 text-center text-gray-500 text-theme-xs dark:text-gray-400 sm:text-sm">
|
|
||||||
Revenue
|
|
||||||
</p>
|
|
||||||
<p className="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
|
||||||
$20K
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M7.60141 2.33683C7.73885 2.18084 7.9401 2.08243 8.16435 2.08243C8.16475 2.08243 8.16516 2.08243 8.16556 2.08243C8.35773 2.08219 8.54998 2.15535 8.69664 2.30191L12.6968 6.29924C12.9898 6.59203 12.9899 7.0669 12.6971 7.3599C12.4044 7.6529 11.9295 7.65306 11.6365 7.36027L8.91435 4.64004L8.91435 13.5C8.91435 13.9142 8.57856 14.25 8.16435 14.25C7.75013 14.25 7.41435 13.9142 7.41435 13.5L7.41435 4.64442L4.69679 7.36025C4.4038 7.65305 3.92893 7.6529 3.63613 7.35992C3.34333 7.06693 3.34348 6.59206 3.63646 6.29926L7.60141 2.33683Z"
|
|
||||||
fill="#039855"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-px bg-gray-200 h-7 dark:bg-gray-800"></div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p className="mb-1 text-center text-gray-500 text-theme-xs dark:text-gray-400 sm:text-sm">
|
|
||||||
Today
|
|
||||||
</p>
|
|
||||||
<p className="flex items-center justify-center gap-1 text-base font-semibold text-gray-800 dark:text-white/90 sm:text-lg">
|
|
||||||
$20K
|
|
||||||
<svg
|
|
||||||
width="16"
|
|
||||||
height="16"
|
|
||||||
viewBox="0 0 16 16"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M7.60141 2.33683C7.73885 2.18084 7.9401 2.08243 8.16435 2.08243C8.16475 2.08243 8.16516 2.08243 8.16556 2.08243C8.35773 2.08219 8.54998 2.15535 8.69664 2.30191L12.6968 6.29924C12.9898 6.59203 12.9899 7.0669 12.6971 7.3599C12.4044 7.6529 11.9295 7.65306 11.6365 7.36027L8.91435 4.64004L8.91435 13.5C8.91435 13.9142 8.57856 14.25 8.16435 14.25C7.75013 14.25 7.41435 13.9142 7.41435 13.5L7.41435 4.64442L4.69679 7.36025C4.4038 7.65305 3.92893 7.6529 3.63613 7.35992C3.34333 7.06693 3.34348 6.59206 3.63646 6.29926L7.60141 2.33683Z"
|
|
||||||
fill="#039855"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "../ui/table";
|
|
||||||
import Badge from "../ui/badge/Badge";
|
|
||||||
|
|
||||||
// Define the TypeScript interface for the table rows
|
|
||||||
interface Product {
|
|
||||||
id: number; // Unique identifier for each product
|
|
||||||
name: string; // Product name
|
|
||||||
variants: string; // Number of variants (e.g., "1 Variant", "2 Variants")
|
|
||||||
category: string; // Category of the product
|
|
||||||
price: string; // Price of the product (as a string with currency symbol)
|
|
||||||
// status: string; // Status of the product
|
|
||||||
image: string; // URL or path to the product image
|
|
||||||
status: "Delivered" | "Pending" | "Canceled"; // Status of the product
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the table data using the interface
|
|
||||||
const tableData: Product[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: "MacBook Pro 13”",
|
|
||||||
variants: "2 Variants",
|
|
||||||
category: "Laptop",
|
|
||||||
price: "$2399.00",
|
|
||||||
status: "Delivered",
|
|
||||||
image: "/images/product/product-01.jpg", // Replace with actual image URL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: "Apple Watch Ultra",
|
|
||||||
variants: "1 Variant",
|
|
||||||
category: "Watch",
|
|
||||||
price: "$879.00",
|
|
||||||
status: "Pending",
|
|
||||||
image: "/images/product/product-02.jpg", // Replace with actual image URL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: "iPhone 15 Pro Max",
|
|
||||||
variants: "2 Variants",
|
|
||||||
category: "SmartPhone",
|
|
||||||
price: "$1869.00",
|
|
||||||
status: "Delivered",
|
|
||||||
image: "/images/product/product-03.jpg", // Replace with actual image URL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: "iPad Pro 3rd Gen",
|
|
||||||
variants: "2 Variants",
|
|
||||||
category: "Electronics",
|
|
||||||
price: "$1699.00",
|
|
||||||
status: "Canceled",
|
|
||||||
image: "/images/product/product-04.jpg", // Replace with actual image URL
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
name: "AirPods Pro 2nd Gen",
|
|
||||||
variants: "1 Variant",
|
|
||||||
category: "Accessories",
|
|
||||||
price: "$240.00",
|
|
||||||
status: "Delivered",
|
|
||||||
image: "/images/product/product-05.jpg", // Replace with actual image URL
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function RecentOrders() {
|
|
||||||
return (
|
|
||||||
<div className="overflow-hidden rounded-2xl border border-gray-200 bg-white px-4 pb-3 pt-4 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6">
|
|
||||||
<div className="flex flex-col gap-2 mb-4 sm:flex-row sm:items-center sm:justify-between">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Recent Orders
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<button className="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
<svg
|
|
||||||
className="stroke-current fill-white dark:fill-gray-800"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M2.29004 5.90393H17.7067"
|
|
||||||
stroke=""
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M17.7075 14.0961H2.29085"
|
|
||||||
stroke=""
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M12.0826 3.33331C13.5024 3.33331 14.6534 4.48431 14.6534 5.90414C14.6534 7.32398 13.5024 8.47498 12.0826 8.47498C10.6627 8.47498 9.51172 7.32398 9.51172 5.90415C9.51172 4.48432 10.6627 3.33331 12.0826 3.33331Z"
|
|
||||||
fill=""
|
|
||||||
stroke=""
|
|
||||||
strokeWidth="1.5"
|
|
||||||
/>
|
|
||||||
<path
|
|
||||||
d="M7.91745 11.525C6.49762 11.525 5.34662 12.676 5.34662 14.0959C5.34661 15.5157 6.49762 16.6667 7.91745 16.6667C9.33728 16.6667 10.4883 15.5157 10.4883 14.0959C10.4883 12.676 9.33728 11.525 7.91745 11.525Z"
|
|
||||||
fill=""
|
|
||||||
stroke=""
|
|
||||||
strokeWidth="1.5"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Filter
|
|
||||||
</button>
|
|
||||||
<button className="inline-flex items-center gap-2 rounded-lg border border-gray-300 bg-white px-4 py-2.5 text-theme-sm font-medium text-gray-700 shadow-theme-xs hover:bg-gray-50 hover:text-gray-800 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-white/[0.03] dark:hover:text-gray-200">
|
|
||||||
See all
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="max-w-full overflow-x-auto">
|
|
||||||
<Table>
|
|
||||||
{/* Table Header */}
|
|
||||||
<TableHeader className="border-gray-100 dark:border-gray-800 border-y">
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Products
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Category
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Price
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
{/* Table Body */}
|
|
||||||
|
|
||||||
<TableBody className="divide-y divide-gray-100 dark:divide-gray-800">
|
|
||||||
{tableData.map((product) => (
|
|
||||||
<TableRow key={product.id} className="">
|
|
||||||
<TableCell className="py-3">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="h-[50px] w-[50px] overflow-hidden rounded-md">
|
|
||||||
<img
|
|
||||||
src={product.image}
|
|
||||||
className="h-[50px] w-[50px]"
|
|
||||||
alt={product.name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<p className="font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
{product.name}
|
|
||||||
</p>
|
|
||||||
<span className="text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
{product.variants}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="py-3 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
{product.price}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="py-3 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
{product.category}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="py-3 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
<Badge
|
|
||||||
size="sm"
|
|
||||||
color={
|
|
||||||
product.status === "Delivered"
|
|
||||||
? "success"
|
|
||||||
: product.status === "Pending"
|
|
||||||
? "warning"
|
|
||||||
: "error"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{product.status}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import Chart from "react-apexcharts";
|
|
||||||
import { ApexOptions } from "apexcharts";
|
|
||||||
import ChartTab from "../common/ChartTab";
|
|
||||||
|
|
||||||
export default function StatisticsChart() {
|
|
||||||
const options: ApexOptions = {
|
|
||||||
legend: {
|
|
||||||
show: false, // Hide legend
|
|
||||||
position: "top",
|
|
||||||
horizontalAlign: "left",
|
|
||||||
},
|
|
||||||
colors: ["#465FFF", "#9CB9FF"], // Define line colors
|
|
||||||
chart: {
|
|
||||||
fontFamily: "Outfit, sans-serif",
|
|
||||||
height: 310,
|
|
||||||
type: "line", // Set the chart type to 'line'
|
|
||||||
toolbar: {
|
|
||||||
show: false, // Hide chart toolbar
|
|
||||||
},
|
|
||||||
},
|
|
||||||
stroke: {
|
|
||||||
curve: "straight", // Define the line style (straight, smooth, or step)
|
|
||||||
width: [2, 2], // Line width for each dataset
|
|
||||||
},
|
|
||||||
|
|
||||||
fill: {
|
|
||||||
type: "gradient",
|
|
||||||
gradient: {
|
|
||||||
opacityFrom: 0.55,
|
|
||||||
opacityTo: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
markers: {
|
|
||||||
size: 0, // Size of the marker points
|
|
||||||
strokeColors: "#fff", // Marker border color
|
|
||||||
strokeWidth: 2,
|
|
||||||
hover: {
|
|
||||||
size: 6, // Marker size on hover
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
xaxis: {
|
|
||||||
lines: {
|
|
||||||
show: false, // Hide grid lines on x-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
lines: {
|
|
||||||
show: true, // Show grid lines on y-axis
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
dataLabels: {
|
|
||||||
enabled: false, // Disable data labels
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: true, // Enable tooltip
|
|
||||||
x: {
|
|
||||||
format: "dd MMM yyyy", // Format for x-axis tooltip
|
|
||||||
},
|
|
||||||
},
|
|
||||||
xaxis: {
|
|
||||||
type: "category", // Category-based x-axis
|
|
||||||
categories: [
|
|
||||||
"Jan",
|
|
||||||
"Feb",
|
|
||||||
"Mar",
|
|
||||||
"Apr",
|
|
||||||
"May",
|
|
||||||
"Jun",
|
|
||||||
"Jul",
|
|
||||||
"Aug",
|
|
||||||
"Sep",
|
|
||||||
"Oct",
|
|
||||||
"Nov",
|
|
||||||
"Dec",
|
|
||||||
],
|
|
||||||
axisBorder: {
|
|
||||||
show: false, // Hide x-axis border
|
|
||||||
},
|
|
||||||
axisTicks: {
|
|
||||||
show: false, // Hide x-axis ticks
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: false, // Disable tooltip for x-axis points
|
|
||||||
},
|
|
||||||
},
|
|
||||||
yaxis: {
|
|
||||||
labels: {
|
|
||||||
style: {
|
|
||||||
fontSize: "12px", // Adjust font size for y-axis labels
|
|
||||||
colors: ["#6B7280"], // Color of the labels
|
|
||||||
},
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
text: "", // Remove y-axis title
|
|
||||||
style: {
|
|
||||||
fontSize: "0px",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const series = [
|
|
||||||
{
|
|
||||||
name: "Sales",
|
|
||||||
data: [180, 190, 170, 160, 175, 165, 170, 205, 230, 210, 240, 235],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Revenue",
|
|
||||||
data: [40, 30, 50, 40, 55, 40, 70, 100, 110, 120, 150, 140],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<div className="rounded-2xl border border-gray-200 bg-white px-5 pb-5 pt-5 dark:border-gray-800 dark:bg-white/[0.03] sm:px-6 sm:pt-6">
|
|
||||||
<div className="flex flex-col gap-5 mb-6 sm:flex-row sm:justify-between">
|
|
||||||
<div className="w-full">
|
|
||||||
<h3 className="text-lg font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
Statistics
|
|
||||||
</h3>
|
|
||||||
<p className="mt-1 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
Target you’ve set for each month
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start w-full gap-3 sm:justify-end">
|
|
||||||
<ChartTab />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="max-w-full overflow-x-auto custom-scrollbar">
|
|
||||||
<div className="min-w-[1000px] xl:min-w-full">
|
|
||||||
<Chart options={options} series={series} type="area" height={310} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import React, { FC, ReactNode, FormEvent } from "react";
|
|
||||||
|
|
||||||
interface FormProps {
|
|
||||||
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
|
|
||||||
children: ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Form: FC<FormProps> = ({ onSubmit, children, className }) => {
|
|
||||||
return (
|
|
||||||
<form
|
|
||||||
onSubmit={(event) => {
|
|
||||||
event.preventDefault(); // Prevent default form submission
|
|
||||||
onSubmit(event);
|
|
||||||
}}
|
|
||||||
className={` ${className}`} // Default spacing between form fields
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Form;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import React, { FC, ReactNode } from "react";
|
|
||||||
import { twMerge } from "tailwind-merge";
|
|
||||||
|
|
||||||
interface LabelProps {
|
|
||||||
htmlFor?: string;
|
|
||||||
children: ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Label: FC<LabelProps> = ({ htmlFor, children, className }) => {
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
htmlFor={htmlFor}
|
|
||||||
className={twMerge(
|
|
||||||
// Default classes that apply by default
|
|
||||||
"mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400",
|
|
||||||
|
|
||||||
// User-defined className that can override the default margin
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Label;
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
interface Option {
|
|
||||||
value: string;
|
|
||||||
text: string;
|
|
||||||
selected: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MultiSelectProps {
|
|
||||||
label: string;
|
|
||||||
options: Option[];
|
|
||||||
defaultSelected?: string[];
|
|
||||||
onChange?: (selected: string[]) => void;
|
|
||||||
disabled?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const MultiSelect: React.FC<MultiSelectProps> = ({
|
|
||||||
label,
|
|
||||||
options,
|
|
||||||
defaultSelected = [],
|
|
||||||
onChange,
|
|
||||||
disabled = false,
|
|
||||||
}) => {
|
|
||||||
const [selectedOptions, setSelectedOptions] =
|
|
||||||
useState<string[]>(defaultSelected);
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
const toggleDropdown = () => {
|
|
||||||
if (disabled) return;
|
|
||||||
setIsOpen((prev) => !prev);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSelect = (optionValue: string) => {
|
|
||||||
const newSelectedOptions = selectedOptions.includes(optionValue)
|
|
||||||
? selectedOptions.filter((value) => value !== optionValue)
|
|
||||||
: [...selectedOptions, optionValue];
|
|
||||||
|
|
||||||
setSelectedOptions(newSelectedOptions);
|
|
||||||
if (onChange) onChange(newSelectedOptions);
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeOption = (index: number, value: string) => {
|
|
||||||
const newSelectedOptions = selectedOptions.filter((opt) => opt !== value);
|
|
||||||
setSelectedOptions(newSelectedOptions);
|
|
||||||
if (onChange) onChange(newSelectedOptions);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectedValuesText = selectedOptions.map(
|
|
||||||
(value) => options.find((option) => option.value === value)?.text || ""
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="w-full">
|
|
||||||
<label className="mb-1.5 block text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="relative z-20 inline-block w-full">
|
|
||||||
<div className="relative flex flex-col items-center">
|
|
||||||
<div onClick={toggleDropdown} className="w-full">
|
|
||||||
<div className="mb-2 flex h-11 rounded-lg border border-gray-300 py-1.5 pl-3 pr-3 shadow-theme-xs outline-none transition focus:border-brand-300 focus:shadow-focus-ring dark:border-gray-700 dark:bg-gray-900 dark:focus:border-brand-300">
|
|
||||||
<div className="flex flex-wrap flex-auto gap-2">
|
|
||||||
{selectedValuesText.length > 0 ? (
|
|
||||||
selectedValuesText.map((text, index) => (
|
|
||||||
<div
|
|
||||||
key={index}
|
|
||||||
className="group flex items-center justify-center rounded-full border-[0.7px] border-transparent bg-gray-100 py-1 pl-2.5 pr-2 text-sm text-gray-800 hover:border-gray-200 dark:bg-gray-800 dark:text-white/90 dark:hover:border-gray-800"
|
|
||||||
>
|
|
||||||
<span className="flex-initial max-w-full">{text}</span>
|
|
||||||
<div className="flex flex-row-reverse flex-auto">
|
|
||||||
<div
|
|
||||||
onClick={() =>
|
|
||||||
removeOption(index, selectedOptions[index])
|
|
||||||
}
|
|
||||||
className="pl-2 text-gray-500 cursor-pointer group-hover:text-gray-400 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
role="button"
|
|
||||||
width="14"
|
|
||||||
height="14"
|
|
||||||
viewBox="0 0 14 14"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.40717 4.46881C3.11428 4.17591 3.11428 3.70104 3.40717 3.40815C3.70006 3.11525 4.17494 3.11525 4.46783 3.40815L6.99943 5.93975L9.53095 3.40822C9.82385 3.11533 10.2987 3.11533 10.5916 3.40822C10.8845 3.70112 10.8845 4.17599 10.5916 4.46888L8.06009 7.00041L10.5916 9.53193C10.8845 9.82482 10.8845 10.2997 10.5916 10.5926C10.2987 10.8855 9.82385 10.8855 9.53095 10.5926L6.99943 8.06107L4.46783 10.5927C4.17494 10.8856 3.70006 10.8856 3.40717 10.5927C3.11428 10.2998 3.11428 9.8249 3.40717 9.53201L5.93877 7.00041L3.40717 4.46881Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
) : (
|
|
||||||
<input
|
|
||||||
placeholder="Select option"
|
|
||||||
className="w-full h-full p-1 pr-2 text-sm bg-transparent border-0 outline-none appearance-none placeholder:text-gray-800 focus:border-0 focus:outline-none focus:ring-0 dark:placeholder:text-white/90"
|
|
||||||
readOnly
|
|
||||||
value="Select option"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center py-1 pl-1 pr-1 w-7">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={toggleDropdown}
|
|
||||||
className="w-5 h-5 text-gray-700 outline-none cursor-pointer focus:outline-none dark:text-gray-400"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className={`stroke-current ${isOpen ? "rotate-180" : ""}`}
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M4.79175 7.39551L10.0001 12.6038L15.2084 7.39551"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isOpen && (
|
|
||||||
<div
|
|
||||||
className="absolute left-0 z-40 w-full overflow-y-auto bg-white rounded-lg shadow top-full max-h-select dark:bg-gray-900"
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
{options.map((option, index) => (
|
|
||||||
<div key={index}>
|
|
||||||
<div
|
|
||||||
className={`hover:bg-primary/5 w-full cursor-pointer rounded-t border-b border-gray-200 dark:border-gray-800`}
|
|
||||||
onClick={() => handleSelect(option.value)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`relative flex w-full items-center p-2 pl-2 ${
|
|
||||||
selectedOptions.includes(option.value)
|
|
||||||
? "bg-primary/10"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="mx-2 leading-6 text-gray-800 dark:text-white/90">
|
|
||||||
{option.text}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MultiSelect;
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
interface Option {
|
|
||||||
value: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SelectProps {
|
|
||||||
options: Option[];
|
|
||||||
placeholder?: string;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
className?: string;
|
|
||||||
defaultValue?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Select: React.FC<SelectProps> = ({
|
|
||||||
options,
|
|
||||||
placeholder = "Select an option",
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
defaultValue = "",
|
|
||||||
}) => {
|
|
||||||
// Manage the selected value
|
|
||||||
const [selectedValue, setSelectedValue] = useState<string>(defaultValue);
|
|
||||||
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setSelectedValue(value);
|
|
||||||
onChange(value); // Trigger parent handler
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<select
|
|
||||||
className={`h-11 w-full appearance-none rounded-lg border border-gray-300 bg-transparent px-4 py-2.5 pr-11 text-sm shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-none focus:ring focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800 ${
|
|
||||||
selectedValue
|
|
||||||
? "text-gray-800 dark:text-white/90"
|
|
||||||
: "text-gray-400 dark:text-gray-400"
|
|
||||||
} ${className}`}
|
|
||||||
value={selectedValue}
|
|
||||||
onChange={handleChange}
|
|
||||||
>
|
|
||||||
{/* Placeholder option */}
|
|
||||||
<option
|
|
||||||
value=""
|
|
||||||
disabled
|
|
||||||
className="text-gray-700 dark:bg-gray-900 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{placeholder}
|
|
||||||
</option>
|
|
||||||
{/* Map over options */}
|
|
||||||
{options.map((option) => (
|
|
||||||
<option
|
|
||||||
key={option.value}
|
|
||||||
value={option.value}
|
|
||||||
className="text-gray-700 dark:bg-gray-900 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{option.label}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Select;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Checkbox from "../input/Checkbox";
|
|
||||||
|
|
||||||
export default function CheckboxComponents() {
|
|
||||||
const [isChecked, setIsChecked] = useState(false);
|
|
||||||
const [isCheckedTwo, setIsCheckedTwo] = useState(true);
|
|
||||||
const [isCheckedDisabled, setIsCheckedDisabled] = useState(true);
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Checkbox">
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Checkbox checked={isChecked} onChange={setIsChecked} />
|
|
||||||
<span className="block text-sm font-medium text-gray-700 dark:text-gray-400">
|
|
||||||
Default
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Checkbox
|
|
||||||
checked={isCheckedTwo}
|
|
||||||
onChange={setIsCheckedTwo}
|
|
||||||
label="Checked"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<Checkbox
|
|
||||||
checked={isCheckedDisabled}
|
|
||||||
onChange={setIsCheckedDisabled}
|
|
||||||
disabled
|
|
||||||
label="Disabled"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Label from "../Label";
|
|
||||||
import Input from "../input/InputField";
|
|
||||||
import Select from "../Select";
|
|
||||||
import { CalenderIcon, EyeCloseIcon, EyeIcon, TimeIcon } from "../../../icons";
|
|
||||||
|
|
||||||
export default function DefaultInputs() {
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
|
||||||
const options = [
|
|
||||||
{ value: "marketing", label: "Marketing" },
|
|
||||||
{ value: "template", label: "Template" },
|
|
||||||
{ value: "development", label: "Development" },
|
|
||||||
];
|
|
||||||
const handleSelectChange = (value: string) => {
|
|
||||||
console.log("Selected value:", value);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Default Inputs">
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<Label>Input</Label>
|
|
||||||
<Input type="text" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Input with Placeholder</Label>
|
|
||||||
<Input type="text" placeholder="info@gmail.com" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Select Input</Label>
|
|
||||||
<Select
|
|
||||||
options={options}
|
|
||||||
placeholder="Select an option"
|
|
||||||
onChange={handleSelectChange}
|
|
||||||
className="dark:bg-dark-900"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Password Input</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
type={showPassword ? "text" : "password"}
|
|
||||||
placeholder="Enter your password"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowPassword(!showPassword)}
|
|
||||||
className="absolute z-30 -translate-y-1/2 cursor-pointer right-4 top-1/2"
|
|
||||||
>
|
|
||||||
{showPassword ? (
|
|
||||||
<EyeIcon className="fill-gray-500 dark:fill-gray-400" />
|
|
||||||
) : (
|
|
||||||
<EyeCloseIcon className="fill-gray-500 dark:fill-gray-400" />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="datePicker">Date Picker Input</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
type="date"
|
|
||||||
id="datePicker"
|
|
||||||
name="datePicker"
|
|
||||||
onChange={(e) => console.log(e.target.value)}
|
|
||||||
/>
|
|
||||||
<span className="absolute text-gray-500 -translate-y-1/2 pointer-events-none right-3 top-1/2 dark:text-gray-400">
|
|
||||||
<CalenderIcon />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="tm">Date Picker Input</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
type="time"
|
|
||||||
id="tm"
|
|
||||||
name="tm"
|
|
||||||
onChange={(e) => console.log(e.target.value)}
|
|
||||||
/>
|
|
||||||
<span className="absolute text-gray-500 -translate-y-1/2 pointer-events-none right-3 top-1/2 dark:text-gray-400">
|
|
||||||
<TimeIcon />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label htmlFor="tm">Input with Payment</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder="Card number"
|
|
||||||
className="pl-[62px]"
|
|
||||||
/>
|
|
||||||
<span className="absolute left-0 top-1/2 flex h-11 w-[46px] -translate-y-1/2 items-center justify-center border-r border-gray-200 dark:border-gray-800">
|
|
||||||
<svg
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<circle cx="6.25" cy="10" r="5.625" fill="#E80B26" />
|
|
||||||
<circle cx="13.75" cy="10" r="5.625" fill="#F59D31" />
|
|
||||||
<path
|
|
||||||
d="M10 14.1924C11.1508 13.1625 11.875 11.6657 11.875 9.99979C11.875 8.33383 11.1508 6.8371 10 5.80713C8.84918 6.8371 8.125 8.33383 8.125 9.99979C8.125 11.6657 8.84918 13.1625 10 14.1924Z"
|
|
||||||
fill="#FC6020"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import { useDropzone } from "react-dropzone";
|
|
||||||
// import Dropzone from "react-dropzone";
|
|
||||||
|
|
||||||
const DropzoneComponent: React.FC = () => {
|
|
||||||
const onDrop = (acceptedFiles: File[]) => {
|
|
||||||
console.log("Files dropped:", acceptedFiles);
|
|
||||||
// Handle file uploads here
|
|
||||||
};
|
|
||||||
|
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
||||||
onDrop,
|
|
||||||
accept: {
|
|
||||||
"image/png": [],
|
|
||||||
"image/jpeg": [],
|
|
||||||
"image/webp": [],
|
|
||||||
"image/svg+xml": [],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Dropzone">
|
|
||||||
<div className="transition border border-gray-300 border-dashed cursor-pointer dark:hover:border-brand-500 dark:border-gray-700 rounded-xl hover:border-brand-500">
|
|
||||||
<form
|
|
||||||
{...getRootProps()}
|
|
||||||
className={`dropzone rounded-xl border-dashed border-gray-300 p-7 lg:p-10
|
|
||||||
${
|
|
||||||
isDragActive
|
|
||||||
? "border-brand-500 bg-gray-100 dark:bg-gray-800"
|
|
||||||
: "border-gray-300 bg-gray-50 dark:border-gray-700 dark:bg-gray-900"
|
|
||||||
}
|
|
||||||
`}
|
|
||||||
id="demo-upload"
|
|
||||||
>
|
|
||||||
{/* Hidden Input */}
|
|
||||||
<input {...getInputProps()} />
|
|
||||||
|
|
||||||
<div className="dz-message flex flex-col items-center !m-0">
|
|
||||||
{/* Icon Container */}
|
|
||||||
<div className="mb-[22px] flex justify-center">
|
|
||||||
<div className="flex h-[68px] w-[68px] items-center justify-center rounded-full bg-gray-200 text-gray-700 dark:bg-gray-800 dark:text-gray-400">
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="29"
|
|
||||||
height="28"
|
|
||||||
viewBox="0 0 29 28"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M14.5019 3.91699C14.2852 3.91699 14.0899 4.00891 13.953 4.15589L8.57363 9.53186C8.28065 9.82466 8.2805 10.2995 8.5733 10.5925C8.8661 10.8855 9.34097 10.8857 9.63396 10.5929L13.7519 6.47752V18.667C13.7519 19.0812 14.0877 19.417 14.5019 19.417C14.9161 19.417 15.2519 19.0812 15.2519 18.667V6.48234L19.3653 10.5929C19.6583 10.8857 20.1332 10.8855 20.426 10.5925C20.7188 10.2995 20.7186 9.82463 20.4256 9.53184L15.0838 4.19378C14.9463 4.02488 14.7367 3.91699 14.5019 3.91699ZM5.91626 18.667C5.91626 18.2528 5.58047 17.917 5.16626 17.917C4.75205 17.917 4.41626 18.2528 4.41626 18.667V21.8337C4.41626 23.0763 5.42362 24.0837 6.66626 24.0837H22.3339C23.5766 24.0837 24.5839 23.0763 24.5839 21.8337V18.667C24.5839 18.2528 24.2482 17.917 23.8339 17.917C23.4197 17.917 23.0839 18.2528 23.0839 18.667V21.8337C23.0839 22.2479 22.7482 22.5837 22.3339 22.5837H6.66626C6.25205 22.5837 5.91626 22.2479 5.91626 21.8337V18.667Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Text Content */}
|
|
||||||
<h4 className="mb-3 font-semibold text-gray-800 text-theme-xl dark:text-white/90">
|
|
||||||
{isDragActive ? "Drop Files Here" : "Drag & Drop Files Here"}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<span className=" text-center mb-5 block w-full max-w-[290px] text-sm text-gray-700 dark:text-gray-400">
|
|
||||||
Drag and drop your PNG, JPG, WebP, SVG images here or browse
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="font-medium underline text-theme-sm text-brand-500">
|
|
||||||
Browse File
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DropzoneComponent;
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import FileInput from "../input/FileInput";
|
|
||||||
import Label from "../Label";
|
|
||||||
|
|
||||||
export default function FileInputExample() {
|
|
||||||
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const file = event.target.files?.[0];
|
|
||||||
if (file) {
|
|
||||||
console.log("Selected file:", file.name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ComponentCard title="File Input">
|
|
||||||
<div>
|
|
||||||
<Label>Upload file</Label>
|
|
||||||
<FileInput onChange={handleFileChange} className="custom-class" />
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Label from "../Label";
|
|
||||||
import Input from "../input/InputField";
|
|
||||||
import { EnvelopeIcon } from "../../../icons";
|
|
||||||
import PhoneInput from "../group-input/PhoneInput";
|
|
||||||
|
|
||||||
export default function InputGroup() {
|
|
||||||
const countries = [
|
|
||||||
{ code: "US", label: "+1" },
|
|
||||||
{ code: "GB", label: "+44" },
|
|
||||||
{ code: "CA", label: "+1" },
|
|
||||||
{ code: "AU", label: "+61" },
|
|
||||||
];
|
|
||||||
const handlePhoneNumberChange = (phoneNumber: string) => {
|
|
||||||
console.log("Updated phone number:", phoneNumber);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Input Group">
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<Label>Email</Label>
|
|
||||||
<div className="relative">
|
|
||||||
<Input
|
|
||||||
placeholder="info@gmail.com"
|
|
||||||
type="text"
|
|
||||||
className="pl-[62px]"
|
|
||||||
/>
|
|
||||||
<span className="absolute left-0 top-1/2 -translate-y-1/2 border-r border-gray-200 px-3.5 py-3 text-gray-500 dark:border-gray-800 dark:text-gray-400">
|
|
||||||
<EnvelopeIcon />
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Phone</Label>
|
|
||||||
<PhoneInput
|
|
||||||
selectPosition="start"
|
|
||||||
countries={countries}
|
|
||||||
placeholder="+1 (555) 000-0000"
|
|
||||||
onChange={handlePhoneNumberChange}
|
|
||||||
/>
|
|
||||||
</div>{" "}
|
|
||||||
<div>
|
|
||||||
<Label>Phone</Label>
|
|
||||||
<PhoneInput
|
|
||||||
selectPosition="end"
|
|
||||||
countries={countries}
|
|
||||||
placeholder="+1 (555) 000-0000"
|
|
||||||
onChange={handlePhoneNumberChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Input from "../input/InputField";
|
|
||||||
import Label from "../Label";
|
|
||||||
|
|
||||||
export default function InputStates() {
|
|
||||||
const [email, setEmail] = useState("");
|
|
||||||
const [error, setError] = useState(false);
|
|
||||||
|
|
||||||
// Simulate a validation check
|
|
||||||
const validateEmail = (value: string) => {
|
|
||||||
const isValidEmail =
|
|
||||||
/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value);
|
|
||||||
setError(!isValidEmail);
|
|
||||||
return isValidEmail;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setEmail(value);
|
|
||||||
validateEmail(value);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ComponentCard
|
|
||||||
title="Input States"
|
|
||||||
desc="Validation styles for error, success and disabled states on form controls."
|
|
||||||
>
|
|
||||||
<div className="space-y-5 sm:space-y-6">
|
|
||||||
{/* Error Input */}
|
|
||||||
<div>
|
|
||||||
<Label>Email</Label>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
error={error}
|
|
||||||
onChange={handleEmailChange}
|
|
||||||
placeholder="Enter your email"
|
|
||||||
hint={error ? "This is an invalid email address." : ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Success Input */}
|
|
||||||
<div>
|
|
||||||
<Label>Email</Label>
|
|
||||||
<Input
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
success={!error}
|
|
||||||
onChange={handleEmailChange}
|
|
||||||
placeholder="Enter your email"
|
|
||||||
hint={!error ? "Valid email!" : ""}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Disabled Input */}
|
|
||||||
<div>
|
|
||||||
<Label>Email</Label>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
value="disabled@example.com"
|
|
||||||
disabled={true}
|
|
||||||
placeholder="Disabled email"
|
|
||||||
hint="This field is disabled."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Radio from "../input/Radio";
|
|
||||||
|
|
||||||
export default function RadioButtons() {
|
|
||||||
const [selectedValue, setSelectedValue] = useState<string>("option2");
|
|
||||||
|
|
||||||
const handleRadioChange = (value: string) => {
|
|
||||||
setSelectedValue(value);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Radio Buttons">
|
|
||||||
<div className="flex flex-wrap items-center gap-8">
|
|
||||||
<Radio
|
|
||||||
id="radio1"
|
|
||||||
name="group1"
|
|
||||||
value="option1"
|
|
||||||
checked={selectedValue === "option1"}
|
|
||||||
onChange={handleRadioChange}
|
|
||||||
label="Default"
|
|
||||||
/>
|
|
||||||
<Radio
|
|
||||||
id="radio2"
|
|
||||||
name="group1"
|
|
||||||
value="option2"
|
|
||||||
checked={selectedValue === "option2"}
|
|
||||||
onChange={handleRadioChange}
|
|
||||||
label="Selected"
|
|
||||||
/>
|
|
||||||
<Radio
|
|
||||||
id="radio3"
|
|
||||||
name="group1"
|
|
||||||
value="option3"
|
|
||||||
checked={selectedValue === "option3"}
|
|
||||||
onChange={handleRadioChange}
|
|
||||||
label="Disabled"
|
|
||||||
disabled={true}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Label from "../Label";
|
|
||||||
import Select from "../Select";
|
|
||||||
import MultiSelect from "../MultiSelect";
|
|
||||||
|
|
||||||
export default function SelectInputs() {
|
|
||||||
const options = [
|
|
||||||
{ value: "marketing", label: "Marketing" },
|
|
||||||
{ value: "template", label: "Template" },
|
|
||||||
{ value: "development", label: "Development" },
|
|
||||||
];
|
|
||||||
const handleSelectChange = (value: string) => {
|
|
||||||
console.log("Selected value:", value);
|
|
||||||
};
|
|
||||||
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
|
||||||
|
|
||||||
const multiOptions = [
|
|
||||||
{ value: "1", text: "Option 1", selected: false },
|
|
||||||
{ value: "2", text: "Option 2", selected: false },
|
|
||||||
{ value: "3", text: "Option 3", selected: false },
|
|
||||||
{ value: "4", text: "Option 4", selected: false },
|
|
||||||
{ value: "5", text: "Option 5", selected: false },
|
|
||||||
];
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Select Inputs">
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<Label>Select Input</Label>
|
|
||||||
<Select
|
|
||||||
options={options}
|
|
||||||
placeholder="Select Option"
|
|
||||||
onChange={handleSelectChange}
|
|
||||||
className="dark:bg-dark-900"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<MultiSelect
|
|
||||||
label="Multiple Select Options"
|
|
||||||
options={multiOptions}
|
|
||||||
defaultSelected={["1", "3"]}
|
|
||||||
onChange={(values) => setSelectedValues(values)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<Label>Selected Values:</Label>
|
|
||||||
<p>{selectedValues.join(", ")}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import TextArea from "../input/TextArea";
|
|
||||||
import Label from "../Label";
|
|
||||||
|
|
||||||
export default function TextAreaInput() {
|
|
||||||
const [message, setMessage] = useState("");
|
|
||||||
const [messageTwo, setMessageTwo] = useState("");
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Textarea input field">
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Default TextArea */}
|
|
||||||
<div>
|
|
||||||
<Label>Description</Label>
|
|
||||||
<TextArea
|
|
||||||
value={message}
|
|
||||||
onChange={(value) => setMessage(value)}
|
|
||||||
rows={6}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Disabled TextArea */}
|
|
||||||
<div>
|
|
||||||
<Label>Description</Label>
|
|
||||||
<TextArea rows={6} disabled />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Error TextArea */}
|
|
||||||
<div>
|
|
||||||
<Label>Description</Label>
|
|
||||||
<TextArea
|
|
||||||
rows={6}
|
|
||||||
value={messageTwo}
|
|
||||||
error
|
|
||||||
onChange={(value) => setMessageTwo(value)}
|
|
||||||
hint="Please enter a valid message."
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import ComponentCard from "../../common/ComponentCard";
|
|
||||||
import Switch from "../switch/Switch";
|
|
||||||
|
|
||||||
export default function ToggleSwitch() {
|
|
||||||
const handleSwitchChange = (checked: boolean) => {
|
|
||||||
console.log("Switch is now:", checked ? "ON" : "OFF");
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<ComponentCard title="Toggle switch input">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Switch
|
|
||||||
label="Default"
|
|
||||||
defaultChecked={true}
|
|
||||||
onChange={handleSwitchChange}
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Checked"
|
|
||||||
defaultChecked={true}
|
|
||||||
onChange={handleSwitchChange}
|
|
||||||
/>
|
|
||||||
<Switch label="Disabled" disabled={true} />
|
|
||||||
</div>{" "}
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Switch
|
|
||||||
label="Default"
|
|
||||||
defaultChecked={true}
|
|
||||||
onChange={handleSwitchChange}
|
|
||||||
color="gray"
|
|
||||||
/>
|
|
||||||
<Switch
|
|
||||||
label="Checked"
|
|
||||||
defaultChecked={true}
|
|
||||||
onChange={handleSwitchChange}
|
|
||||||
color="gray"
|
|
||||||
/>
|
|
||||||
<Switch label="Disabled" disabled={true} color="gray" />
|
|
||||||
</div>
|
|
||||||
</ComponentCard>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
interface CountryCode {
|
|
||||||
code: string;
|
|
||||||
label: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PhoneInputProps {
|
|
||||||
countries: CountryCode[];
|
|
||||||
placeholder?: string;
|
|
||||||
onChange?: (phoneNumber: string) => void;
|
|
||||||
selectPosition?: "start" | "end"; // New prop for dropdown position
|
|
||||||
}
|
|
||||||
|
|
||||||
const PhoneInput: React.FC<PhoneInputProps> = ({
|
|
||||||
countries,
|
|
||||||
placeholder = "+1 (555) 000-0000",
|
|
||||||
onChange,
|
|
||||||
selectPosition = "start", // Default position is 'start'
|
|
||||||
}) => {
|
|
||||||
const [selectedCountry, setSelectedCountry] = useState<string>("US");
|
|
||||||
const [phoneNumber, setPhoneNumber] = useState<string>("+1");
|
|
||||||
|
|
||||||
const countryCodes: Record<string, string> = countries.reduce(
|
|
||||||
(acc, { code, label }) => ({ ...acc, [code]: label }),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCountryChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
|
||||||
const newCountry = e.target.value;
|
|
||||||
setSelectedCountry(newCountry);
|
|
||||||
setPhoneNumber(countryCodes[newCountry]);
|
|
||||||
if (onChange) {
|
|
||||||
onChange(countryCodes[newCountry]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePhoneNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
||||||
const newPhoneNumber = e.target.value;
|
|
||||||
setPhoneNumber(newPhoneNumber);
|
|
||||||
if (onChange) {
|
|
||||||
onChange(newPhoneNumber);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative flex">
|
|
||||||
{/* Dropdown position: Start */}
|
|
||||||
{selectPosition === "start" && (
|
|
||||||
<div className="absolute">
|
|
||||||
<select
|
|
||||||
value={selectedCountry}
|
|
||||||
onChange={handleCountryChange}
|
|
||||||
className="appearance-none bg-none rounded-l-lg border-0 border-r border-gray-200 bg-transparent py-3 pl-3.5 pr-8 leading-tight text-gray-700 focus:border-brand-300 focus:outline-none focus:ring focus:ring-brand-500/10 dark:border-gray-800 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{countries.map((country) => (
|
|
||||||
<option
|
|
||||||
key={country.code}
|
|
||||||
value={country.code}
|
|
||||||
className="text-gray-700 dark:bg-gray-900 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{country.code}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<div className="absolute inset-y-0 flex items-center text-gray-700 pointer-events-none bg-none right-3 dark:text-gray-400">
|
|
||||||
<svg
|
|
||||||
className="stroke-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Input field */}
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
value={phoneNumber}
|
|
||||||
onChange={handlePhoneNumberChange}
|
|
||||||
placeholder={placeholder}
|
|
||||||
className={`dark:bg-dark-900 h-11 w-full ${
|
|
||||||
selectPosition === "start" ? "pl-[84px]" : "pr-[84px]"
|
|
||||||
} rounded-lg border border-gray-300 bg-transparent py-3 px-4 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-none focus:ring focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Dropdown position: End */}
|
|
||||||
{selectPosition === "end" && (
|
|
||||||
<div className="absolute right-0">
|
|
||||||
<select
|
|
||||||
value={selectedCountry}
|
|
||||||
onChange={handleCountryChange}
|
|
||||||
className="appearance-none bg-none rounded-r-lg border-0 border-l border-gray-200 bg-transparent py-3 pl-3.5 pr-8 leading-tight text-gray-700 focus:border-brand-300 focus:outline-none focus:ring focus:ring-brand-500/10 dark:border-gray-800 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{countries.map((country) => (
|
|
||||||
<option
|
|
||||||
key={country.code}
|
|
||||||
value={country.code}
|
|
||||||
className="text-gray-700 dark:bg-gray-900 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{country.code}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<div className="absolute inset-y-0 flex items-center text-gray-700 pointer-events-none right-3 dark:text-gray-400">
|
|
||||||
<svg
|
|
||||||
className="stroke-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M4.79175 7.396L10.0001 12.6043L15.2084 7.396"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default PhoneInput;
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface CheckboxProps {
|
|
||||||
label?: string; // Optional label for the checkbox
|
|
||||||
checked: boolean; // Checked state
|
|
||||||
className?: string;
|
|
||||||
id?: string; // Unique ID for the checkbox
|
|
||||||
onChange: (checked: boolean) => void; // Change handler
|
|
||||||
disabled?: boolean; // Disabled state
|
|
||||||
}
|
|
||||||
|
|
||||||
const Checkbox: React.FC<CheckboxProps> = ({
|
|
||||||
label,
|
|
||||||
checked,
|
|
||||||
id,
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
disabled = false,
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
className={`flex items-center space-x-3 cursor-pointer ${
|
|
||||||
disabled ? "cursor-not-allowed opacity-60" : ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id={id}
|
|
||||||
type="checkbox"
|
|
||||||
className={`w-4 h-4 ${className} dark:bg-gray-800 dark:border-gray-700 border-gray-300 dark:focus:outline-none rounded text-brand-500 dark:focus:ring-0 focus:ring-0 dark:focus:ring-transparent focus:ring-transparent focus:outline-none dark:focus:bg-outline-none focus:ring-offset-0`}
|
|
||||||
checked={checked}
|
|
||||||
onChange={(e) => onChange(e.target.checked)}
|
|
||||||
disabled={disabled}
|
|
||||||
/>
|
|
||||||
{label && (
|
|
||||||
<span className="font-medium text-gray-800 text-theme-sm dark:text-white">
|
|
||||||
{label}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Checkbox;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import React, { FC } from "react";
|
|
||||||
|
|
||||||
interface FileInputProps {
|
|
||||||
className?: string;
|
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const FileInput: FC<FileInputProps> = ({ className, onChange }) => {
|
|
||||||
return (
|
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
className={`focus:border-ring-brand-300 h-11 w-full overflow-hidden rounded-lg border border-gray-300 bg-transparent text-sm text-gray-500 shadow-theme-xs transition-colors file:mr-5 file:border-collapse file:cursor-pointer file:rounded-l-lg file:border-0 file:border-r file:border-solid file:border-gray-200 file:bg-gray-50 file:py-3 file:pl-3.5 file:pr-3 file:text-sm file:text-gray-700 placeholder:text-gray-400 hover:file:bg-gray-100 focus:outline-none focus:file:ring-brand-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-400 dark:text-white/90 dark:file:border-gray-800 dark:file:bg-white/[0.03] dark:file:text-gray-400 dark:placeholder:text-gray-400 ${className}`}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FileInput;
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
import React, { FC } from "react";
|
|
||||||
|
|
||||||
interface InputProps {
|
|
||||||
type?: "text" | "number" | "email" | "password" | "date" | "time" | string;
|
|
||||||
id?: string;
|
|
||||||
name?: string;
|
|
||||||
placeholder?: string;
|
|
||||||
value?: string | number;
|
|
||||||
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
||||||
className?: string;
|
|
||||||
min?: string;
|
|
||||||
max?: string;
|
|
||||||
step?: number;
|
|
||||||
disabled?: boolean;
|
|
||||||
success?: boolean;
|
|
||||||
error?: boolean;
|
|
||||||
hint?: string; // Optional hint text
|
|
||||||
}
|
|
||||||
|
|
||||||
const Input: FC<InputProps> = ({
|
|
||||||
type = "text",
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
placeholder,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
min,
|
|
||||||
max,
|
|
||||||
step,
|
|
||||||
disabled = false,
|
|
||||||
success = false,
|
|
||||||
error = false,
|
|
||||||
hint,
|
|
||||||
}) => {
|
|
||||||
// Determine input styles based on state (disabled, success, error)
|
|
||||||
let inputClasses = `h-11 w-full rounded-lg border appearance-none px-4 py-2.5 text-sm shadow-theme-xs placeholder:text-gray-400 focus:outline-none focus:ring dark:bg-gray-900 dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800 ${className}`;
|
|
||||||
|
|
||||||
// Add styles for the different states
|
|
||||||
if (disabled) {
|
|
||||||
inputClasses += ` text-gray-500 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:text-gray-400 dark:border-gray-700`;
|
|
||||||
} else if (error) {
|
|
||||||
inputClasses += ` text-error-800 border-error-500 focus:ring focus:ring-error-500/10 dark:text-error-400 dark:border-error-500`;
|
|
||||||
} else if (success) {
|
|
||||||
inputClasses += ` text-success-500 border-success-400 focus:ring-success-500/10 focus:border-success-300 dark:text-success-400 dark:border-success-500`;
|
|
||||||
} else {
|
|
||||||
inputClasses += ` bg-transparent text-gray-800 border-gray-300 focus:border-brand-300 focus:ring focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:focus:border-brand-800`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<input
|
|
||||||
type={type}
|
|
||||||
id={id}
|
|
||||||
name={name}
|
|
||||||
placeholder={placeholder}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
min={min}
|
|
||||||
max={max}
|
|
||||||
step={step}
|
|
||||||
disabled={disabled}
|
|
||||||
className={inputClasses}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Optional Hint Text */}
|
|
||||||
{hint && (
|
|
||||||
<p
|
|
||||||
className={`mt-1.5 text-xs ${
|
|
||||||
error
|
|
||||||
? "text-error-500"
|
|
||||||
: success
|
|
||||||
? "text-success-500"
|
|
||||||
: "text-gray-500"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{hint}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Input;
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface RadioProps {
|
|
||||||
id: string; // Unique ID for the radio button
|
|
||||||
name: string; // Radio group name
|
|
||||||
value: string; // Value of the radio button
|
|
||||||
checked: boolean; // Whether the radio button is checked
|
|
||||||
label: string; // Label for the radio button
|
|
||||||
onChange: (value: string) => void; // Handler for value change
|
|
||||||
className?: string; // Optional additional classes
|
|
||||||
disabled?: boolean; // Optional disabled state for the radio button
|
|
||||||
}
|
|
||||||
|
|
||||||
const Radio: React.FC<RadioProps> = ({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
checked,
|
|
||||||
label,
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
disabled = false,
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
htmlFor={id}
|
|
||||||
className={`relative flex cursor-pointer select-none items-center gap-3 text-sm font-medium ${
|
|
||||||
disabled
|
|
||||||
? "text-gray-300 dark:text-gray-600 cursor-not-allowed"
|
|
||||||
: "text-gray-700 dark:text-gray-400"
|
|
||||||
} ${className}`}
|
|
||||||
>
|
|
||||||
<input
|
|
||||||
id={id}
|
|
||||||
name={name}
|
|
||||||
type="radio"
|
|
||||||
value={value}
|
|
||||||
checked={checked}
|
|
||||||
onChange={() => !disabled && onChange(value)} // Prevent onChange when disabled
|
|
||||||
className="sr-only"
|
|
||||||
disabled={disabled} // Disable input
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className={`flex h-5 w-5 items-center justify-center rounded-full border-[1.25px] ${
|
|
||||||
checked
|
|
||||||
? "border-brand-500 bg-brand-500"
|
|
||||||
: "bg-transparent border-gray-300 dark:border-gray-700"
|
|
||||||
} ${
|
|
||||||
disabled
|
|
||||||
? "bg-gray-100 dark:bg-gray-700 border-gray-200 dark:border-gray-700"
|
|
||||||
: ""
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`h-2 w-2 rounded-full bg-white ${
|
|
||||||
checked ? "block" : "hidden"
|
|
||||||
}`}
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Radio;
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface RadioProps {
|
|
||||||
id: string; // Unique ID for the radio button
|
|
||||||
name: string; // Group name for the radio button
|
|
||||||
value: string; // Value of the radio button
|
|
||||||
checked: boolean; // Whether the radio button is checked
|
|
||||||
label: string; // Label text for the radio button
|
|
||||||
onChange: (value: string) => void; // Handler for when the radio button is toggled
|
|
||||||
className?: string; // Optional custom classes for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
const RadioSm: React.FC<RadioProps> = ({
|
|
||||||
id,
|
|
||||||
name,
|
|
||||||
value,
|
|
||||||
checked,
|
|
||||||
label,
|
|
||||||
onChange,
|
|
||||||
className = "",
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
htmlFor={id}
|
|
||||||
className={`flex cursor-pointer select-none items-center text-sm text-gray-500 dark:text-gray-400 ${className}`}
|
|
||||||
>
|
|
||||||
<span className="relative">
|
|
||||||
{/* Hidden Input */}
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id={id}
|
|
||||||
name={name}
|
|
||||||
value={value}
|
|
||||||
checked={checked}
|
|
||||||
onChange={() => onChange(value)}
|
|
||||||
className="sr-only"
|
|
||||||
/>
|
|
||||||
{/* Styled Radio Circle */}
|
|
||||||
<span
|
|
||||||
className={`mr-2 flex h-4 w-4 items-center justify-center rounded-full border ${
|
|
||||||
checked
|
|
||||||
? "border-brand-500 bg-brand-500"
|
|
||||||
: "bg-transparent border-gray-300 dark:border-gray-700"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{/* Inner Dot */}
|
|
||||||
<span
|
|
||||||
className={`h-1.5 w-1.5 rounded-full ${
|
|
||||||
checked ? "bg-white" : "bg-white dark:bg-[#1e2636]"
|
|
||||||
}`}
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RadioSm;
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface TextareaProps {
|
|
||||||
placeholder?: string; // Placeholder text
|
|
||||||
rows?: number; // Number of rows
|
|
||||||
value?: string; // Current value
|
|
||||||
onChange?: (value: string) => void; // Change handler
|
|
||||||
className?: string; // Additional CSS classes
|
|
||||||
disabled?: boolean; // Disabled state
|
|
||||||
error?: boolean; // Error state
|
|
||||||
hint?: string; // Hint text to display
|
|
||||||
}
|
|
||||||
|
|
||||||
const TextArea: React.FC<TextareaProps> = ({
|
|
||||||
placeholder = "Enter your message", // Default placeholder
|
|
||||||
rows = 3, // Default number of rows
|
|
||||||
value = "", // Default value
|
|
||||||
onChange, // Callback for changes
|
|
||||||
className = "", // Additional custom styles
|
|
||||||
disabled = false, // Disabled state
|
|
||||||
error = false, // Error state
|
|
||||||
hint = "", // Default hint text
|
|
||||||
}) => {
|
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
|
||||||
if (onChange) {
|
|
||||||
onChange(e.target.value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let textareaClasses = `w-full rounded-lg border px-4 py-2.5 text-sm shadow-theme-xs focus:outline-none ${className}`;
|
|
||||||
|
|
||||||
if (disabled) {
|
|
||||||
textareaClasses += ` bg-gray-100 opacity-50 text-gray-500 border-gray-300 cursor-not-allowed dark:bg-gray-800 dark:text-gray-400 dark:border-gray-700`;
|
|
||||||
} else if (error) {
|
|
||||||
textareaClasses += ` bg-transparent text-gray-400 border-gray-300 focus:border-error-300 focus:ring focus:ring-error-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:focus:border-error-800`;
|
|
||||||
} else {
|
|
||||||
textareaClasses += ` bg-transparent text-gray-400 border-gray-300 focus:border-brand-300 focus:ring focus:ring-brand-500/10 dark:border-gray-700 dark:bg-gray-900 dark:text-white/90 dark:focus:border-brand-800`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<textarea
|
|
||||||
placeholder={placeholder}
|
|
||||||
rows={rows}
|
|
||||||
value={value}
|
|
||||||
onChange={handleChange}
|
|
||||||
disabled={disabled}
|
|
||||||
className={textareaClasses}
|
|
||||||
/>
|
|
||||||
{hint && (
|
|
||||||
<p
|
|
||||||
className={`mt-2 text-sm ${
|
|
||||||
error ? "text-error-500" : "text-gray-500 dark:text-gray-400"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{hint}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default TextArea;
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
interface SwitchProps {
|
|
||||||
label: string;
|
|
||||||
defaultChecked?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
onChange?: (checked: boolean) => void;
|
|
||||||
color?: "blue" | "gray"; // Added prop to toggle color theme
|
|
||||||
}
|
|
||||||
|
|
||||||
const Switch: React.FC<SwitchProps> = ({
|
|
||||||
label,
|
|
||||||
defaultChecked = false,
|
|
||||||
disabled = false,
|
|
||||||
onChange,
|
|
||||||
color = "blue", // Default to blue color
|
|
||||||
}) => {
|
|
||||||
const [isChecked, setIsChecked] = useState(defaultChecked);
|
|
||||||
|
|
||||||
const handleToggle = () => {
|
|
||||||
if (disabled) return;
|
|
||||||
const newCheckedState = !isChecked;
|
|
||||||
setIsChecked(newCheckedState);
|
|
||||||
if (onChange) {
|
|
||||||
onChange(newCheckedState);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const switchColors =
|
|
||||||
color === "blue"
|
|
||||||
? {
|
|
||||||
background: isChecked
|
|
||||||
? "bg-brand-500 "
|
|
||||||
: "bg-gray-200 dark:bg-white/10", // Blue version
|
|
||||||
knob: isChecked
|
|
||||||
? "translate-x-full bg-white"
|
|
||||||
: "translate-x-0 bg-white",
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
background: isChecked
|
|
||||||
? "bg-gray-800 dark:bg-white/10"
|
|
||||||
: "bg-gray-200 dark:bg-white/10", // Gray version
|
|
||||||
knob: isChecked
|
|
||||||
? "translate-x-full bg-white"
|
|
||||||
: "translate-x-0 bg-white",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<label
|
|
||||||
className={`flex cursor-pointer select-none items-center gap-3 text-sm font-medium ${
|
|
||||||
disabled ? "text-gray-400" : "text-gray-700 dark:text-gray-400"
|
|
||||||
}`}
|
|
||||||
onClick={handleToggle} // Toggle when the label itself is clicked
|
|
||||||
>
|
|
||||||
<div className="relative">
|
|
||||||
<div
|
|
||||||
className={`block transition duration-150 ease-linear h-6 w-11 rounded-full ${
|
|
||||||
disabled
|
|
||||||
? "bg-gray-100 pointer-events-none dark:bg-gray-800"
|
|
||||||
: switchColors.background
|
|
||||||
}`}
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
className={`absolute left-0.5 top-0.5 h-5 w-5 rounded-full shadow-theme-sm duration-150 ease-linear transform ${switchColors.knob}`}
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
{label}
|
|
||||||
</label>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Switch;
|
|
||||||
@@ -1,189 +0,0 @@
|
|||||||
import React, { useEffect, useRef, useState } from "react";
|
|
||||||
import { ThemeToggleButton } from "../common/ThemeToggleButton";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
import NotificationDropdown from "./NotificationDropdown";
|
|
||||||
import UserDropdown from "./UserDropdown";
|
|
||||||
|
|
||||||
// Define the interface for the props
|
|
||||||
interface HeaderProps {
|
|
||||||
onClick?: () => void; // Optional function that takes no arguments and returns void
|
|
||||||
onToggle: () => void;
|
|
||||||
}
|
|
||||||
const Header: React.FC<HeaderProps> = ({ onClick, onToggle }) => {
|
|
||||||
const [isApplicationMenuOpen, setApplicationMenuOpen] = useState(false);
|
|
||||||
|
|
||||||
const toggleApplicationMenu = () => {
|
|
||||||
setApplicationMenuOpen(!isApplicationMenuOpen);
|
|
||||||
};
|
|
||||||
|
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if ((event.metaKey || event.ctrlKey) && event.key === "k") {
|
|
||||||
event.preventDefault();
|
|
||||||
inputRef.current?.focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("keydown", handleKeyDown);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("keydown", handleKeyDown);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
return (
|
|
||||||
<header className="sticky top-0 flex w-full bg-white border-gray-200 z-99999 dark:border-gray-800 dark:bg-gray-900 lg:border-b">
|
|
||||||
<div className="flex flex-col items-center justify-between flex-grow lg:flex-row lg:px-6">
|
|
||||||
<div className="flex items-center justify-between w-full gap-2 px-3 py-3 border-b border-gray-200 dark:border-gray-800 sm:gap-4 lg:justify-normal lg:border-b-0 lg:px-0 lg:py-4">
|
|
||||||
<button
|
|
||||||
className="block w-10 h-10 text-gray-500 lg:hidden dark:text-gray-400"
|
|
||||||
onClick={onToggle}
|
|
||||||
>
|
|
||||||
{/* Hamburger Icon */}
|
|
||||||
<svg
|
|
||||||
className={`block`}
|
|
||||||
width="16"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 16 12"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M0.583252 1C0.583252 0.585788 0.919038 0.25 1.33325 0.25H14.6666C15.0808 0.25 15.4166 0.585786 15.4166 1C15.4166 1.41421 15.0808 1.75 14.6666 1.75L1.33325 1.75C0.919038 1.75 0.583252 1.41422 0.583252 1ZM0.583252 11C0.583252 10.5858 0.919038 10.25 1.33325 10.25L14.6666 10.25C15.0808 10.25 15.4166 10.5858 15.4166 11C15.4166 11.4142 15.0808 11.75 14.6666 11.75L1.33325 11.75C0.919038 11.75 0.583252 11.4142 0.583252 11ZM1.33325 5.25C0.919038 5.25 0.583252 5.58579 0.583252 6C0.583252 6.41421 0.919038 6.75 1.33325 6.75L7.99992 6.75C8.41413 6.75 8.74992 6.41421 8.74992 6C8.74992 5.58579 8.41413 5.25 7.99992 5.25L1.33325 5.25Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<svg
|
|
||||||
className="hidden"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
{/* Cross Icon */}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={onClick}
|
|
||||||
className="items-center justify-center hidden w-10 h-10 text-gray-500 border-gray-200 rounded-lg z-99999 dark:border-gray-800 lg:flex dark:text-gray-400 lg:h-11 lg:w-11 lg:border"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="hidden fill-current lg:block"
|
|
||||||
width="16"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 16 12"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M0.583252 1C0.583252 0.585788 0.919038 0.25 1.33325 0.25H14.6666C15.0808 0.25 15.4166 0.585786 15.4166 1C15.4166 1.41421 15.0808 1.75 14.6666 1.75L1.33325 1.75C0.919038 1.75 0.583252 1.41422 0.583252 1ZM0.583252 11C0.583252 10.5858 0.919038 10.25 1.33325 10.25L14.6666 10.25C15.0808 10.25 15.4166 10.5858 15.4166 11C15.4166 11.4142 15.0808 11.75 14.6666 11.75L1.33325 11.75C0.919038 11.75 0.583252 11.4142 0.583252 11ZM1.33325 5.25C0.919038 5.25 0.583252 5.58579 0.583252 6C0.583252 6.41421 0.919038 6.75 1.33325 6.75L7.99992 6.75C8.41413 6.75 8.74992 6.41421 8.74992 6C8.74992 5.58579 8.41413 5.25 7.99992 5.25L1.33325 5.25Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Link to="/" className="lg:hidden">
|
|
||||||
<img
|
|
||||||
className="dark:hidden"
|
|
||||||
src="./images/logo/logo.svg"
|
|
||||||
alt="Logo"
|
|
||||||
/>
|
|
||||||
<img
|
|
||||||
className="hidden dark:block"
|
|
||||||
src="./images/logo/logo-dark.svg"
|
|
||||||
alt="Logo"
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={toggleApplicationMenu}
|
|
||||||
className="flex items-center justify-center w-10 h-10 text-gray-700 rounded-lg z-99999 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800 lg:hidden"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M5.99902 10.4951C6.82745 10.4951 7.49902 11.1667 7.49902 11.9951V12.0051C7.49902 12.8335 6.82745 13.5051 5.99902 13.5051C5.1706 13.5051 4.49902 12.8335 4.49902 12.0051V11.9951C4.49902 11.1667 5.1706 10.4951 5.99902 10.4951ZM17.999 10.4951C18.8275 10.4951 19.499 11.1667 19.499 11.9951V12.0051C19.499 12.8335 18.8275 13.5051 17.999 13.5051C17.1706 13.5051 16.499 12.8335 16.499 12.0051V11.9951C16.499 11.1667 17.1706 10.4951 17.999 10.4951ZM13.499 11.9951C13.499 11.1667 12.8275 10.4951 11.999 10.4951C11.1706 10.4951 10.499 11.1667 10.499 11.9951V12.0051C10.499 12.8335 11.1706 13.5051 11.999 13.5051C12.8275 13.5051 13.499 12.8335 13.499 12.0051V11.9951Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div className="hidden lg:block">
|
|
||||||
<form>
|
|
||||||
<div className="relative">
|
|
||||||
<span className="absolute -translate-y-1/2 pointer-events-none left-4 top-1/2">
|
|
||||||
<svg
|
|
||||||
className="fill-gray-500 dark:fill-gray-400"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.04175 9.37363C3.04175 5.87693 5.87711 3.04199 9.37508 3.04199C12.8731 3.04199 15.7084 5.87693 15.7084 9.37363C15.7084 12.8703 12.8731 15.7053 9.37508 15.7053C5.87711 15.7053 3.04175 12.8703 3.04175 9.37363ZM9.37508 1.54199C5.04902 1.54199 1.54175 5.04817 1.54175 9.37363C1.54175 13.6991 5.04902 17.2053 9.37508 17.2053C11.2674 17.2053 13.003 16.5344 14.357 15.4176L17.177 18.238C17.4699 18.5309 17.9448 18.5309 18.2377 18.238C18.5306 17.9451 18.5306 17.4703 18.2377 17.1774L15.418 14.3573C16.5365 13.0033 17.2084 11.2669 17.2084 9.37363C17.2084 5.04817 13.7011 1.54199 9.37508 1.54199Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
ref={inputRef}
|
|
||||||
type="text"
|
|
||||||
placeholder="Search or type command..."
|
|
||||||
aria-label="Search or type command"
|
|
||||||
className="dark:bg-dark-900 h-11 w-full rounded-lg border border-gray-200 bg-transparent py-2.5 pl-12 pr-14 text-sm text-gray-800 shadow-theme-xs placeholder:text-gray-400 focus:border-brand-300 focus:outline-none focus:ring focus:ring-brand-500/10 dark:border-gray-800 dark:bg-gray-900 dark:bg-white/[0.03] dark:text-white/90 dark:placeholder:text-white/30 dark:focus:border-brand-800 xl:w-[430px]"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
aria-label="Search shortcut key: Command + K"
|
|
||||||
className="absolute right-2.5 top-1/2 inline-flex -translate-y-1/2 items-center gap-0.5 rounded-lg border border-gray-200 bg-gray-50 px-[7px] py-[4.5px] text-xs -tracking-[0.2px] text-gray-500 dark:border-gray-800 dark:bg-white/[0.03] dark:text-gray-400 focus:ring-2 focus:ring-brand-500 focus:outline-none"
|
|
||||||
>
|
|
||||||
<span> ⌘ </span>
|
|
||||||
<span> K </span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={`${
|
|
||||||
isApplicationMenuOpen ? "flex" : "hidden"
|
|
||||||
} items-center justify-between w-full gap-4 px-5 py-4 lg:flex shadow-theme-md lg:justify-end lg:px-0 lg:shadow-none`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2 2xsm:gap-3">
|
|
||||||
{/* <!-- Dark Mode Toggler --> */}
|
|
||||||
<ThemeToggleButton />
|
|
||||||
{/* <!-- Dark Mode Toggler --> */}
|
|
||||||
<NotificationDropdown />
|
|
||||||
{/* <!-- Notification Menu Area --> */}
|
|
||||||
</div>
|
|
||||||
{/* <!-- User Area --> */}
|
|
||||||
<UserDropdown />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Header;
|
|
||||||
@@ -1,380 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
|
|
||||||
export default function NotificationDropdown() {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
const [notifying, setNotifying] = useState(true);
|
|
||||||
|
|
||||||
function toggleDropdown() {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDropdown() {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
toggleDropdown();
|
|
||||||
setNotifying(false);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
className="relative flex items-center justify-center text-gray-500 transition-colors bg-white border border-gray-200 rounded-full hover:text-gray-700 h-11 w-11 hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-900 dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-white"
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
className={`absolute right-0 top-0.5 z-10 h-2 w-2 rounded-full bg-orange-400 ${
|
|
||||||
!notifying ? "hidden" : "flex"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<span className="absolute inline-flex w-full h-full bg-orange-400 rounded-full opacity-75 animate-ping"></span>
|
|
||||||
</span>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="20"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M10.75 2.29248C10.75 1.87827 10.4143 1.54248 10 1.54248C9.58583 1.54248 9.25004 1.87827 9.25004 2.29248V2.83613C6.08266 3.20733 3.62504 5.9004 3.62504 9.16748V14.4591H3.33337C2.91916 14.4591 2.58337 14.7949 2.58337 15.2091C2.58337 15.6234 2.91916 15.9591 3.33337 15.9591H4.37504H15.625H16.6667C17.0809 15.9591 17.4167 15.6234 17.4167 15.2091C17.4167 14.7949 17.0809 14.4591 16.6667 14.4591H16.375V9.16748C16.375 5.9004 13.9174 3.20733 10.75 2.83613V2.29248ZM14.875 14.4591V9.16748C14.875 6.47509 12.6924 4.29248 10 4.29248C7.30765 4.29248 5.12504 6.47509 5.12504 9.16748V14.4591H14.875ZM8.00004 17.7085C8.00004 18.1228 8.33583 18.4585 8.75004 18.4585H11.25C11.6643 18.4585 12 18.1228 12 17.7085C12 17.2943 11.6643 16.9585 11.25 16.9585H8.75004C8.33583 16.9585 8.00004 17.2943 8.00004 17.7085Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<Dropdown
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={closeDropdown}
|
|
||||||
className="absolute -right-[240px] mt-[17px] flex h-[480px] w-[350px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark sm:w-[361px] lg:right-0"
|
|
||||||
>
|
|
||||||
<div className="flex items-center justify-between pb-3 mb-3 border-b border-gray-100 dark:border-gray-700">
|
|
||||||
<h5 className="text-lg font-semibold text-gray-800 dark:text-gray-200">
|
|
||||||
Notification
|
|
||||||
</h5>
|
|
||||||
<button
|
|
||||||
onClick={toggleDropdown}
|
|
||||||
className="text-gray-500 transition dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M6.21967 7.28131C5.92678 6.98841 5.92678 6.51354 6.21967 6.22065C6.51256 5.92775 6.98744 5.92775 7.28033 6.22065L11.999 10.9393L16.7176 6.22078C17.0105 5.92789 17.4854 5.92788 17.7782 6.22078C18.0711 6.51367 18.0711 6.98855 17.7782 7.28144L13.0597 12L17.7782 16.7186C18.0711 17.0115 18.0711 17.4863 17.7782 17.7792C17.4854 18.0721 17.0105 18.0721 16.7176 17.7792L11.999 13.0607L7.28033 17.7794C6.98744 18.0722 6.51256 18.0722 6.21967 17.7794C5.92678 17.4865 5.92678 17.0116 6.21967 16.7187L10.9384 12L6.21967 7.28131Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ul className="flex flex-col h-auto overflow-y-auto custom-scrollbar">
|
|
||||||
{/* Example notification items */}
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-02.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90 ">
|
|
||||||
Terry Franci
|
|
||||||
</span>
|
|
||||||
<span>requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>5 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-03.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Alena Franci
|
|
||||||
</span>
|
|
||||||
<span>requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>8 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-04.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block space-x-1 text-theme-sm text-gray-500 dark:text-gray-400">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Jocelyn Kenter
|
|
||||||
</span>
|
|
||||||
<span>requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>15 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
href="/"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-05.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-error-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Brandon Philips
|
|
||||||
</span>
|
|
||||||
<span> requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>1 hr ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-02.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Terry Franci
|
|
||||||
</span>
|
|
||||||
<span> requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>5 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-03.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Alena Franci
|
|
||||||
</span>
|
|
||||||
<span> requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>8 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-04.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="w-full overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-success-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Jocelyn Kenter
|
|
||||||
</span>
|
|
||||||
<span> requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>15 min ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
className="flex gap-3 rounded-lg border-b border-gray-100 p-3 px-4.5 py-3 hover:bg-gray-100 dark:border-gray-800 dark:hover:bg-white/5"
|
|
||||||
>
|
|
||||||
<span className="relative block w-full h-10 rounded-full z-1 max-w-10">
|
|
||||||
<img
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
src="/images/user/user-05.jpg"
|
|
||||||
alt="User"
|
|
||||||
className="overflow-hidden rounded-full"
|
|
||||||
/>
|
|
||||||
<span className="absolute bottom-0 right-0 z-10 h-2.5 w-full max-w-2.5 rounded-full border-[1.5px] border-white bg-error-500 dark:border-gray-900"></span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block">
|
|
||||||
<span className="mb-1.5 block text-theme-sm text-gray-500 dark:text-gray-400 space-x-1">
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Brandon Philips
|
|
||||||
</span>
|
|
||||||
<span> requests permission to change</span>
|
|
||||||
<span className="font-medium text-gray-800 dark:text-white/90">
|
|
||||||
Project - Nganter App
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="flex items-center gap-2 text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
<span>Project</span>
|
|
||||||
<span className="w-1 h-1 bg-gray-400 rounded-full"></span>
|
|
||||||
<span>1 hr ago</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
{/* Add more items as needed */}
|
|
||||||
</ul>
|
|
||||||
<Link
|
|
||||||
to="/"
|
|
||||||
className="block px-4 py-2 mt-3 text-sm font-medium text-center text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-100 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700"
|
|
||||||
>
|
|
||||||
View All Notifications
|
|
||||||
</Link>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
import React, { useState } from "react";
|
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
|
|
||||||
export default function UserDropdown() {
|
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
|
||||||
|
|
||||||
function toggleDropdown() {
|
|
||||||
setIsOpen(!isOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeDropdown() {
|
|
||||||
setIsOpen(false);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<button
|
|
||||||
onClick={toggleDropdown}
|
|
||||||
className="flex items-center text-gray-700 dark:text-gray-400"
|
|
||||||
>
|
|
||||||
<span className="mr-3 overflow-hidden rounded-full h-11 w-11">
|
|
||||||
<img src="/images/user/owner.jpg" alt="User" />
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<span className="block mr-1 font-medium text-theme-sm">Musharof</span>
|
|
||||||
|
|
||||||
<svg
|
|
||||||
className={`stroke-gray-500 dark:stroke-gray-400 transition-transform duration-200 ${
|
|
||||||
isOpen ? "rotate-180" : ""
|
|
||||||
}`}
|
|
||||||
width="18"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 18 20"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M4.3125 8.65625L9 13.3437L13.6875 8.65625"
|
|
||||||
stroke="currentColor"
|
|
||||||
strokeWidth="1.5"
|
|
||||||
strokeLinecap="round"
|
|
||||||
strokeLinejoin="round"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Dropdown
|
|
||||||
isOpen={isOpen}
|
|
||||||
onClose={closeDropdown}
|
|
||||||
className="absolute right-0 mt-[17px] flex w-[260px] flex-col rounded-2xl border border-gray-200 bg-white p-3 shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<span className="block font-medium text-gray-700 text-theme-sm dark:text-gray-400">
|
|
||||||
Musharof Chowdhury
|
|
||||||
</span>
|
|
||||||
<span className="mt-0.5 block text-theme-xs text-gray-500 dark:text-gray-400">
|
|
||||||
randomuser@pimjo.com
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<ul className="flex flex-col gap-1 pt-4 pb-3 border-b border-gray-200 dark:border-gray-800">
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
tag="a"
|
|
||||||
href="/profile"
|
|
||||||
className="flex items-center gap-3 px-3 py-2 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-gray-500 group-hover:fill-gray-700 dark:fill-gray-400 dark:group-hover:fill-gray-300"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M12 3.5C7.30558 3.5 3.5 7.30558 3.5 12C3.5 14.1526 4.3002 16.1184 5.61936 17.616C6.17279 15.3096 8.24852 13.5955 10.7246 13.5955H13.2746C15.7509 13.5955 17.8268 15.31 18.38 17.6167C19.6996 16.119 20.5 14.153 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5ZM17.0246 18.8566V18.8455C17.0246 16.7744 15.3457 15.0955 13.2746 15.0955H10.7246C8.65354 15.0955 6.97461 16.7744 6.97461 18.8455V18.856C8.38223 19.8895 10.1198 20.5 12 20.5C13.8798 20.5 15.6171 19.8898 17.0246 18.8566ZM2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12ZM11.9991 7.25C10.8847 7.25 9.98126 8.15342 9.98126 9.26784C9.98126 10.3823 10.8847 11.2857 11.9991 11.2857C13.1135 11.2857 14.0169 10.3823 14.0169 9.26784C14.0169 8.15342 13.1135 7.25 11.9991 7.25ZM8.48126 9.26784C8.48126 7.32499 10.0563 5.75 11.9991 5.75C13.9419 5.75 15.5169 7.32499 15.5169 9.26784C15.5169 11.2107 13.9419 12.7857 11.9991 12.7857C10.0563 12.7857 8.48126 11.2107 8.48126 9.26784Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Edit profile
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
tag="a"
|
|
||||||
href="/profile"
|
|
||||||
className="flex items-center gap-3 px-3 py-2 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-gray-500 group-hover:fill-gray-700 dark:fill-gray-400 dark:group-hover:fill-gray-300"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M10.4858 3.5L13.5182 3.5C13.9233 3.5 14.2518 3.82851 14.2518 4.23377C14.2518 5.9529 16.1129 7.02795 17.602 6.1682C17.9528 5.96567 18.4014 6.08586 18.6039 6.43667L20.1203 9.0631C20.3229 9.41407 20.2027 9.86286 19.8517 10.0655C18.3625 10.9253 18.3625 13.0747 19.8517 13.9345C20.2026 14.1372 20.3229 14.5859 20.1203 14.9369L18.6039 17.5634C18.4013 17.9142 17.9528 18.0344 17.602 17.8318C16.1129 16.9721 14.2518 18.0471 14.2518 19.7663C14.2518 20.1715 13.9233 20.5 13.5182 20.5H10.4858C10.0804 20.5 9.75182 20.1714 9.75182 19.766C9.75182 18.0461 7.88983 16.9717 6.40067 17.8314C6.04945 18.0342 5.60037 17.9139 5.39767 17.5628L3.88167 14.937C3.67903 14.586 3.79928 14.1372 4.15026 13.9346C5.63949 13.0748 5.63946 10.9253 4.15025 10.0655C3.79926 9.86282 3.67901 9.41401 3.88165 9.06303L5.39764 6.43725C5.60034 6.08617 6.04943 5.96581 6.40065 6.16858C7.88982 7.02836 9.75182 5.9539 9.75182 4.23399C9.75182 3.82862 10.0804 3.5 10.4858 3.5ZM13.5182 2L10.4858 2C9.25201 2 8.25182 3.00019 8.25182 4.23399C8.25182 4.79884 7.64013 5.15215 7.15065 4.86955C6.08213 4.25263 4.71559 4.61859 4.0986 5.68725L2.58261 8.31303C1.96575 9.38146 2.33183 10.7477 3.40025 11.3645C3.88948 11.647 3.88947 12.3531 3.40026 12.6355C2.33184 13.2524 1.96578 14.6186 2.58263 15.687L4.09863 18.3128C4.71562 19.3814 6.08215 19.7474 7.15067 19.1305C7.64015 18.8479 8.25182 19.2012 8.25182 19.766C8.25182 20.9998 9.25201 22 10.4858 22H13.5182C14.7519 22 15.7518 20.9998 15.7518 19.7663C15.7518 19.2015 16.3632 18.8487 16.852 19.1309C17.9202 19.7476 19.2862 19.3816 19.9029 18.3134L21.4193 15.6869C22.0361 14.6185 21.6701 13.2523 20.6017 12.6355C20.1125 12.3531 20.1125 11.647 20.6017 11.3645C21.6701 10.7477 22.0362 9.38152 21.4193 8.3131L19.903 5.68667C19.2862 4.61842 17.9202 4.25241 16.852 4.86917C16.3632 5.15138 15.7518 4.79856 15.7518 4.23377C15.7518 3.00024 14.7519 2 13.5182 2ZM9.6659 11.9999C9.6659 10.7103 10.7113 9.66493 12.0009 9.66493C13.2905 9.66493 14.3359 10.7103 14.3359 11.9999C14.3359 13.2895 13.2905 14.3349 12.0009 14.3349C10.7113 14.3349 9.6659 13.2895 9.6659 11.9999ZM12.0009 8.16493C9.88289 8.16493 8.1659 9.88191 8.1659 11.9999C8.1659 14.1179 9.88289 15.8349 12.0009 15.8349C14.1189 15.8349 15.8359 14.1179 15.8359 11.9999C15.8359 9.88191 14.1189 8.16493 12.0009 8.16493Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Account settings
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<DropdownItem
|
|
||||||
onItemClick={closeDropdown}
|
|
||||||
tag="a"
|
|
||||||
href="/profile"
|
|
||||||
className="flex items-center gap-3 px-3 py-2 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-gray-500 group-hover:fill-gray-700 dark:fill-gray-400 dark:group-hover:fill-gray-300"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.5 12C3.5 7.30558 7.30558 3.5 12 3.5C16.6944 3.5 20.5 7.30558 20.5 12C20.5 16.6944 16.6944 20.5 12 20.5C7.30558 20.5 3.5 16.6944 3.5 12ZM12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2ZM11.0991 7.52507C11.0991 8.02213 11.5021 8.42507 11.9991 8.42507H12.0001C12.4972 8.42507 12.9001 8.02213 12.9001 7.52507C12.9001 7.02802 12.4972 6.62507 12.0001 6.62507H11.9991C11.5021 6.62507 11.0991 7.02802 11.0991 7.52507ZM12.0001 17.3714C11.5859 17.3714 11.2501 17.0356 11.2501 16.6214V10.9449C11.2501 10.5307 11.5859 10.1949 12.0001 10.1949C12.4143 10.1949 12.7501 10.5307 12.7501 10.9449V16.6214C12.7501 17.0356 12.4143 17.3714 12.0001 17.3714Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Support
|
|
||||||
</DropdownItem>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<Link
|
|
||||||
to="/signin"
|
|
||||||
className="flex items-center gap-3 px-3 py-2 mt-3 font-medium text-gray-700 rounded-lg group text-theme-sm hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-gray-300"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
className="fill-gray-500 group-hover:fill-gray-700 dark:group-hover:fill-gray-300"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M15.1007 19.247C14.6865 19.247 14.3507 18.9112 14.3507 18.497L14.3507 14.245H12.8507V18.497C12.8507 19.7396 13.8581 20.747 15.1007 20.747H18.5007C19.7434 20.747 20.7507 19.7396 20.7507 18.497L20.7507 5.49609C20.7507 4.25345 19.7433 3.24609 18.5007 3.24609H15.1007C13.8581 3.24609 12.8507 4.25345 12.8507 5.49609V9.74501L14.3507 9.74501V5.49609C14.3507 5.08188 14.6865 4.74609 15.1007 4.74609L18.5007 4.74609C18.9149 4.74609 19.2507 5.08188 19.2507 5.49609L19.2507 18.497C19.2507 18.9112 18.9149 19.247 18.5007 19.247H15.1007ZM3.25073 11.9984C3.25073 12.2144 3.34204 12.4091 3.48817 12.546L8.09483 17.1556C8.38763 17.4485 8.86251 17.4487 9.15549 17.1559C9.44848 16.8631 9.44863 16.3882 9.15583 16.0952L5.81116 12.7484L16.0007 12.7484C16.4149 12.7484 16.7507 12.4127 16.7507 11.9984C16.7507 11.5842 16.4149 11.2484 16.0007 11.2484L5.81528 11.2484L9.15585 7.90554C9.44864 7.61255 9.44847 7.13767 9.15547 6.84488C8.86248 6.55209 8.3876 6.55226 8.09481 6.84525L3.52309 11.4202C3.35673 11.5577 3.25073 11.7657 3.25073 11.9984Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Sign out
|
|
||||||
</Link>
|
|
||||||
</Dropdown>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableBody,
|
|
||||||
TableCell,
|
|
||||||
TableHeader,
|
|
||||||
TableRow,
|
|
||||||
} from "../ui/table";
|
|
||||||
|
|
||||||
import Badge from "../ui/badge/Badge";
|
|
||||||
|
|
||||||
interface Order {
|
|
||||||
id: number;
|
|
||||||
user: {
|
|
||||||
image: string;
|
|
||||||
name: string;
|
|
||||||
role: string;
|
|
||||||
};
|
|
||||||
projectName: string;
|
|
||||||
team: {
|
|
||||||
images: string[];
|
|
||||||
};
|
|
||||||
status: string;
|
|
||||||
budget: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define the table data using the interface
|
|
||||||
const tableData: Order[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
user: {
|
|
||||||
image: "/images/user/user-17.jpg",
|
|
||||||
name: "Lindsey Curtis",
|
|
||||||
role: "Web Designer",
|
|
||||||
},
|
|
||||||
projectName: "Agency Website",
|
|
||||||
team: {
|
|
||||||
images: [
|
|
||||||
"/images/user/user-22.jpg",
|
|
||||||
"/images/user/user-23.jpg",
|
|
||||||
"/images/user/user-24.jpg",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
budget: "3.9K",
|
|
||||||
status: "Active",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
user: {
|
|
||||||
image: "/images/user/user-18.jpg",
|
|
||||||
name: "Kaiya George",
|
|
||||||
role: "Project Manager",
|
|
||||||
},
|
|
||||||
projectName: "Technology",
|
|
||||||
team: {
|
|
||||||
images: ["/images/user/user-25.jpg", "/images/user/user-26.jpg"],
|
|
||||||
},
|
|
||||||
budget: "24.9K",
|
|
||||||
status: "Pending",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
user: {
|
|
||||||
image: "/images/user/user-17.jpg",
|
|
||||||
name: "Zain Geidt",
|
|
||||||
role: "Content Writing",
|
|
||||||
},
|
|
||||||
projectName: "Blog Writing",
|
|
||||||
team: {
|
|
||||||
images: ["/images/user/user-27.jpg"],
|
|
||||||
},
|
|
||||||
budget: "12.7K",
|
|
||||||
status: "Active",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
user: {
|
|
||||||
image: "/images/user/user-20.jpg",
|
|
||||||
name: "Abram Schleifer",
|
|
||||||
role: "Digital Marketer",
|
|
||||||
},
|
|
||||||
projectName: "Social Media",
|
|
||||||
team: {
|
|
||||||
images: [
|
|
||||||
"/images/user/user-28.jpg",
|
|
||||||
"/images/user/user-29.jpg",
|
|
||||||
"/images/user/user-30.jpg",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
budget: "2.8K",
|
|
||||||
status: "Cancel",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
user: {
|
|
||||||
image: "/images/user/user-21.jpg",
|
|
||||||
name: "Carla George",
|
|
||||||
role: "Front-end Developer",
|
|
||||||
},
|
|
||||||
projectName: "Website",
|
|
||||||
team: {
|
|
||||||
images: [
|
|
||||||
"/images/user/user-31.jpg",
|
|
||||||
"/images/user/user-32.jpg",
|
|
||||||
"/images/user/user-33.jpg",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
budget: "4.5K",
|
|
||||||
status: "Active",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function BasicTableOne() {
|
|
||||||
return (
|
|
||||||
<div className="overflow-hidden rounded-xl border border-gray-200 bg-white dark:border-white/[0.05] dark:bg-white/[0.03]">
|
|
||||||
<div className="max-w-full overflow-x-auto">
|
|
||||||
<div className="min-w-[1102px]">
|
|
||||||
<Table>
|
|
||||||
{/* Table Header */}
|
|
||||||
<TableHeader className="border-b border-gray-100 dark:border-white/[0.05]">
|
|
||||||
<TableRow>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
User
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Project Name
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Team
|
|
||||||
</TableCell>{" "}
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</TableCell>
|
|
||||||
<TableCell
|
|
||||||
isHeader
|
|
||||||
className="px-5 py-3 font-medium text-gray-500 text-start text-theme-xs dark:text-gray-400"
|
|
||||||
>
|
|
||||||
Budget
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
|
|
||||||
{/* Table Body */}
|
|
||||||
<TableBody className="divide-y divide-gray-100 dark:divide-white/[0.05]">
|
|
||||||
{tableData.map((order) => (
|
|
||||||
<TableRow key={order.id}>
|
|
||||||
<TableCell className="px-5 py-4 sm:px-6 text-start">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="w-10 h-10 overflow-hidden rounded-full">
|
|
||||||
<img src={order.user.image} alt={order.user.name} />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span className="block font-medium text-gray-800 text-theme-sm dark:text-white/90">
|
|
||||||
{order.user.name}
|
|
||||||
</span>
|
|
||||||
<span className="block text-gray-500 text-theme-xs dark:text-gray-400">
|
|
||||||
{order.user.role}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="px-4 py-3 text-gray-500 text-start text-theme-sm dark:text-gray-400">
|
|
||||||
{order.projectName}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="px-4 py-3 text-gray-500 text-start text-theme-sm dark:text-gray-400">
|
|
||||||
<div className="flex -space-x-2">
|
|
||||||
{order.team.images.map((teamImage, index) => (
|
|
||||||
<div className="w-6 h-6 overflow-hidden border-2 border-white rounded-full dark:border-gray-900">
|
|
||||||
<img
|
|
||||||
key={index}
|
|
||||||
src={teamImage}
|
|
||||||
alt={`Team member ${index + 1}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="px-4 py-3 text-gray-500 text-start text-theme-sm dark:text-gray-400">
|
|
||||||
<Badge
|
|
||||||
size="sm"
|
|
||||||
color={
|
|
||||||
order.status === "Active"
|
|
||||||
? "success"
|
|
||||||
: order.status === "Pending"
|
|
||||||
? "warning"
|
|
||||||
: "error"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{order.status}
|
|
||||||
</Badge>
|
|
||||||
</TableCell>
|
|
||||||
<TableCell className="px-4 py-3 text-gray-500 text-theme-sm dark:text-gray-400">
|
|
||||||
{order.budget}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
))}
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
66
geradoresfe/src/components/ui/Button.tsx
Normal file
66
geradoresfe/src/components/ui/Button.tsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// components/ui/Button.tsx
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
|
icon?: ReactNode;
|
||||||
|
variant?: "primary" | "secondary" | "ghost" | "danger";
|
||||||
|
loading?: boolean;
|
||||||
|
loadingText?: string;
|
||||||
|
fullWidth?: boolean;
|
||||||
|
rounded?: boolean | "md" | "lg" | "xl" | "2xl" | "full";
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Button({
|
||||||
|
icon,
|
||||||
|
variant = "primary",
|
||||||
|
children,
|
||||||
|
className = "",
|
||||||
|
type = "button",
|
||||||
|
loading = false,
|
||||||
|
loadingText,
|
||||||
|
disabled,
|
||||||
|
fullWidth = false,
|
||||||
|
rounded = "2xl",
|
||||||
|
...rest
|
||||||
|
}: ButtonProps) {
|
||||||
|
const base = twMerge(
|
||||||
|
"inline-flex items-center justify-center gap-2 px-5 py-2.5 font-medium text-sm transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed shadow-sm hover:shadow-md",
|
||||||
|
fullWidth && "w-full",
|
||||||
|
typeof rounded === "string" ? `rounded-${rounded}` : rounded === true ? "rounded-md" : "rounded-2xl"
|
||||||
|
);
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
primary:
|
||||||
|
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500 dark:bg-blue-600 dark:hover:bg-blue-500",
|
||||||
|
secondary:
|
||||||
|
"bg-zinc-100 text-zinc-900 hover:bg-zinc-200 focus:ring-zinc-400 dark:bg-zinc-700 dark:text-white dark:hover:bg-zinc-600",
|
||||||
|
ghost:
|
||||||
|
"bg-transparent text-zinc-700 hover:bg-zinc-100 focus:ring-zinc-300 dark:text-zinc-200 dark:hover:bg-zinc-800",
|
||||||
|
danger:
|
||||||
|
"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500 dark:bg-red-600 dark:hover:bg-red-500",
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type={type}
|
||||||
|
className={twMerge(base, variants[variant], className)}
|
||||||
|
disabled={disabled || loading}
|
||||||
|
aria-busy={loading}
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
{loading ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="animate-spin w-4 h-4" />
|
||||||
|
{loadingText && <span>{loadingText}</span>}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{icon && <span className="flex items-center">{icon}</span>}
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
5
geradoresfe/src/components/ui/ButtonGroup.tsx
Normal file
5
geradoresfe/src/components/ui/ButtonGroup.tsx
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { ReactNode } from "react";
|
||||||
|
|
||||||
|
export function ButtonGroup({ children }: { children: ReactNode }) {
|
||||||
|
return <div className="flex flex-wrap w-full gap-x-2">{children}</div>;
|
||||||
|
}
|
||||||
27
geradoresfe/src/components/ui/DarkModeToggle.tsx
Normal file
27
geradoresfe/src/components/ui/DarkModeToggle.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import { SunIcon, MoonIcon } from "@heroicons/react/24/outline";
|
||||||
|
import { useTheme } from "../../context/ThemeContext";
|
||||||
|
|
||||||
|
export function DarkModeToggle() {
|
||||||
|
const { theme, toggleTheme } = useTheme();
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
console.log("Toggle clicado, tema antes:", theme);
|
||||||
|
toggleTheme();
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("Render DarkModeToggle, theme =", theme);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleToggle}
|
||||||
|
className="p-2 text-gray-700 hover:text-blue-500 dark:text-gray-300"
|
||||||
|
aria-label="Alternar modo escuro"
|
||||||
|
>
|
||||||
|
{theme === "dark" ? (
|
||||||
|
<SunIcon className="w-6 h-6" />
|
||||||
|
) : (
|
||||||
|
<MoonIcon className="w-6 h-6" />
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
15
geradoresfe/src/components/ui/Input.tsx
Normal file
15
geradoresfe/src/components/ui/Input.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
|
||||||
|
|
||||||
|
export function Input({ className = "", ...props }: InputProps) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
className={
|
||||||
|
"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 " +
|
||||||
|
className
|
||||||
|
}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
31
geradoresfe/src/components/ui/ResultDisplay.tsx
Normal file
31
geradoresfe/src/components/ui/ResultDisplay.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { CheckIcon, XIcon } from "lucide-react";
|
||||||
|
|
||||||
|
type ResultDisplayProps = {
|
||||||
|
value: string;
|
||||||
|
validated: boolean | null;
|
||||||
|
onChange?: (value: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ResultDisplay({ value, validated, onChange }: ResultDisplayProps) {
|
||||||
|
const baseClasses = "flex items-center gap-2 px-3 py-2 border rounded-md text-sm w-full";
|
||||||
|
const colors =
|
||||||
|
validated === true
|
||||||
|
? "border-green-500 text-green-700 bg-green-50 dark:text-green-400 dark:bg-green-950 dark:border-green-700"
|
||||||
|
: validated === false
|
||||||
|
? "border-red-500 text-red-700 bg-red-50 dark:text-red-400 dark:bg-red-950 dark:border-red-700"
|
||||||
|
: "border-gray-300 text-gray-700 bg-gray-100 dark:text-gray-300 dark:bg-zinc-800 dark:border-gray-600";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={baseClasses + " " + colors}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
readOnly={!onChange}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange?.(e.target.value)}
|
||||||
|
className="flex-1 bg-transparent outline-none font-mono truncate"
|
||||||
|
/>
|
||||||
|
{validated === true && <CheckIcon className="w-5 h-5" />}
|
||||||
|
{validated === false && <XIcon className="w-5 h-5" />}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
35
geradoresfe/src/components/ui/Select.tsx
Normal file
35
geradoresfe/src/components/ui/Select.tsx
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// components/ui/Select.tsx
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SelectProps {
|
||||||
|
label?: string;
|
||||||
|
options: Option[];
|
||||||
|
value: string;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Select = ({ label, options, value, onChange, placeholder }: SelectProps) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
{label && <label className="mb-1 font-medium text-gray-700 dark:text-gray-300">{label}</label>}
|
||||||
|
<select
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="border border-gray-300 rounded-md px-3 py-2 bg-white dark:bg-zinc-800 text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500 transition"
|
||||||
|
>
|
||||||
|
<option value="" disabled>{placeholder || "Selecionar..."}</option>
|
||||||
|
{options.map((opt) => (
|
||||||
|
<option key={opt.value} value={opt.value}>
|
||||||
|
{opt.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
|
|
||||||
interface AlertProps {
|
|
||||||
variant: "success" | "error" | "warning" | "info"; // Alert type
|
|
||||||
title: string; // Title of the alert
|
|
||||||
message: string; // Message of the alert
|
|
||||||
showLink?: boolean; // Whether to show the "Learn More" link
|
|
||||||
linkHref?: string; // Link URL
|
|
||||||
linkText?: string; // Link text
|
|
||||||
}
|
|
||||||
|
|
||||||
const Alert: React.FC<AlertProps> = ({
|
|
||||||
variant,
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
showLink = false,
|
|
||||||
linkHref = "#",
|
|
||||||
linkText = "Learn more",
|
|
||||||
}) => {
|
|
||||||
// Tailwind classes for each variant
|
|
||||||
const variantClasses = {
|
|
||||||
success: {
|
|
||||||
container:
|
|
||||||
"border-success-500 bg-success-50 dark:border-success-500/30 dark:bg-success-500/15",
|
|
||||||
icon: "text-success-500",
|
|
||||||
},
|
|
||||||
error: {
|
|
||||||
container:
|
|
||||||
"border-error-500 bg-error-50 dark:border-error-500/30 dark:bg-error-500/15",
|
|
||||||
icon: "text-error-500",
|
|
||||||
},
|
|
||||||
warning: {
|
|
||||||
container:
|
|
||||||
"border-warning-500 bg-warning-50 dark:border-warning-500/30 dark:bg-warning-500/15",
|
|
||||||
icon: "text-warning-500",
|
|
||||||
},
|
|
||||||
info: {
|
|
||||||
container:
|
|
||||||
"border-blue-light-500 bg-blue-light-50 dark:border-blue-light-500/30 dark:bg-blue-light-500/15",
|
|
||||||
icon: "text-blue-light-500",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Icon for each variant
|
|
||||||
const icons = {
|
|
||||||
success: (
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.70186 12.0001C3.70186 7.41711 7.41711 3.70186 12.0001 3.70186C16.5831 3.70186 20.2984 7.41711 20.2984 12.0001C20.2984 16.5831 16.5831 20.2984 12.0001 20.2984C7.41711 20.2984 3.70186 16.5831 3.70186 12.0001ZM12.0001 1.90186C6.423 1.90186 1.90186 6.423 1.90186 12.0001C1.90186 17.5772 6.423 22.0984 12.0001 22.0984C17.5772 22.0984 22.0984 17.5772 22.0984 12.0001C22.0984 6.423 17.5772 1.90186 12.0001 1.90186ZM15.6197 10.7395C15.9712 10.388 15.9712 9.81819 15.6197 9.46672C15.2683 9.11525 14.6984 9.11525 14.347 9.46672L11.1894 12.6243L9.6533 11.0883C9.30183 10.7368 8.73198 10.7368 8.38051 11.0883C8.02904 11.4397 8.02904 12.0096 8.38051 12.3611L10.553 14.5335C10.7217 14.7023 10.9507 14.7971 11.1894 14.7971C11.428 14.7971 11.657 14.7023 11.8257 14.5335L15.6197 10.7395Z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
error: (
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M20.3499 12.0004C20.3499 16.612 16.6115 20.3504 11.9999 20.3504C7.38832 20.3504 3.6499 16.612 3.6499 12.0004C3.6499 7.38881 7.38833 3.65039 11.9999 3.65039C16.6115 3.65039 20.3499 7.38881 20.3499 12.0004ZM11.9999 22.1504C17.6056 22.1504 22.1499 17.6061 22.1499 12.0004C22.1499 6.3947 17.6056 1.85039 11.9999 1.85039C6.39421 1.85039 1.8499 6.3947 1.8499 12.0004C1.8499 17.6061 6.39421 22.1504 11.9999 22.1504ZM13.0008 16.4753C13.0008 15.923 12.5531 15.4753 12.0008 15.4753L11.9998 15.4753C11.4475 15.4753 10.9998 15.923 10.9998 16.4753C10.9998 17.0276 11.4475 17.4753 11.9998 17.4753L12.0008 17.4753C12.5531 17.4753 13.0008 17.0276 13.0008 16.4753ZM11.9998 6.62898C12.414 6.62898 12.7498 6.96476 12.7498 7.37898L12.7498 13.0555C12.7498 13.4697 12.414 13.8055 11.9998 13.8055C11.5856 13.8055 11.2498 13.4697 11.2498 13.0555L11.2498 7.37898C11.2498 6.96476 11.5856 6.62898 11.9998 6.62898Z"
|
|
||||||
fill="#F04438"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
warning: (
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.6501 12.0001C3.6501 7.38852 7.38852 3.6501 12.0001 3.6501C16.6117 3.6501 20.3501 7.38852 20.3501 12.0001C20.3501 16.6117 16.6117 20.3501 12.0001 20.3501C7.38852 20.3501 3.6501 16.6117 3.6501 12.0001ZM12.0001 1.8501C6.39441 1.8501 1.8501 6.39441 1.8501 12.0001C1.8501 17.6058 6.39441 22.1501 12.0001 22.1501C17.6058 22.1501 22.1501 17.6058 22.1501 12.0001C22.1501 6.39441 17.6058 1.8501 12.0001 1.8501ZM10.9992 7.52517C10.9992 8.07746 11.4469 8.52517 11.9992 8.52517H12.0002C12.5525 8.52517 13.0002 8.07746 13.0002 7.52517C13.0002 6.97289 12.5525 6.52517 12.0002 6.52517H11.9992C11.4469 6.52517 10.9992 6.97289 10.9992 7.52517ZM12.0002 17.3715C11.586 17.3715 11.2502 17.0357 11.2502 16.6215V10.945C11.2502 10.5308 11.586 10.195 12.0002 10.195C12.4144 10.195 12.7502 10.5308 12.7502 10.945V16.6215C12.7502 17.0357 12.4144 17.3715 12.0002 17.3715Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
info: (
|
|
||||||
<svg
|
|
||||||
className="fill-current"
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M3.6501 11.9996C3.6501 7.38803 7.38852 3.64961 12.0001 3.64961C16.6117 3.64961 20.3501 7.38803 20.3501 11.9996C20.3501 16.6112 16.6117 20.3496 12.0001 20.3496C7.38852 20.3496 3.6501 16.6112 3.6501 11.9996ZM12.0001 1.84961C6.39441 1.84961 1.8501 6.39392 1.8501 11.9996C1.8501 17.6053 6.39441 22.1496 12.0001 22.1496C17.6058 22.1496 22.1501 17.6053 22.1501 11.9996C22.1501 6.39392 17.6058 1.84961 12.0001 1.84961ZM10.9992 7.52468C10.9992 8.07697 11.4469 8.52468 11.9992 8.52468H12.0002C12.5525 8.52468 13.0002 8.07697 13.0002 7.52468C13.0002 6.9724 12.5525 6.52468 12.0002 6.52468H11.9992C11.4469 6.52468 10.9992 6.9724 10.9992 7.52468ZM12.0002 17.371C11.586 17.371 11.2502 17.0352 11.2502 16.621V10.9445C11.2502 10.5303 11.586 10.1945 12.0002 10.1945C12.4144 10.1945 12.7502 10.5303 12.7502 10.9445V16.621C12.7502 17.0352 12.4144 17.371 12.0002 17.371Z"
|
|
||||||
fill=""
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`rounded-xl border p-4 ${variantClasses[variant].container}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<div className={`-mt-0.5 ${variantClasses[variant].icon}`}>
|
|
||||||
{icons[variant]}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h4 className="mb-1 text-sm font-semibold text-gray-800 dark:text-white/90">
|
|
||||||
{title}
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<p className="text-sm text-gray-500 dark:text-gray-400">{message}</p>
|
|
||||||
|
|
||||||
{showLink && (
|
|
||||||
<Link
|
|
||||||
to={linkHref}
|
|
||||||
className="inline-block mt-3 text-sm font-medium text-gray-500 underline dark:text-gray-400"
|
|
||||||
>
|
|
||||||
{linkText}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Alert;
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface AvatarProps {
|
|
||||||
src: string; // URL of the avatar image
|
|
||||||
alt?: string; // Alt text for the avatar
|
|
||||||
size?: "xsmall" | "small" | "medium" | "large" | "xlarge" | "xxlarge"; // Avatar size
|
|
||||||
status?: "online" | "offline" | "busy" | "none"; // Status indicator
|
|
||||||
}
|
|
||||||
|
|
||||||
const sizeClasses = {
|
|
||||||
xsmall: "h-6 w-6 max-w-6",
|
|
||||||
small: "h-8 w-8 max-w-8",
|
|
||||||
medium: "h-10 w-10 max-w-10",
|
|
||||||
large: "h-12 w-12 max-w-12",
|
|
||||||
xlarge: "h-14 w-14 max-w-14",
|
|
||||||
xxlarge: "h-16 w-16 max-w-16",
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusSizeClasses = {
|
|
||||||
xsmall: "h-1.5 w-1.5 max-w-1.5",
|
|
||||||
small: "h-2 w-2 max-w-2",
|
|
||||||
medium: "h-2.5 w-2.5 max-w-2.5",
|
|
||||||
large: "h-3 w-3 max-w-3",
|
|
||||||
xlarge: "h-3.5 w-3.5 max-w-3.5",
|
|
||||||
xxlarge: "h-4 w-4 max-w-4",
|
|
||||||
};
|
|
||||||
|
|
||||||
const statusColorClasses = {
|
|
||||||
online: "bg-success-500",
|
|
||||||
offline: "bg-error-400",
|
|
||||||
busy: "bg-warning-500",
|
|
||||||
};
|
|
||||||
|
|
||||||
const Avatar: React.FC<AvatarProps> = ({
|
|
||||||
src,
|
|
||||||
alt = "User Avatar",
|
|
||||||
size = "medium",
|
|
||||||
status = "none",
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className={`relative rounded-full ${sizeClasses[size]}`}>
|
|
||||||
{/* Avatar Image */}
|
|
||||||
<img src={src} alt={alt} className="object-cover rounded-full" />
|
|
||||||
|
|
||||||
{/* Status Indicator */}
|
|
||||||
{status !== "none" && (
|
|
||||||
<span
|
|
||||||
className={`absolute bottom-0 right-0 rounded-full border-[1.5px] border-white dark:border-gray-900 ${
|
|
||||||
statusSizeClasses[size]
|
|
||||||
} ${statusColorClasses[status] || ""}`}
|
|
||||||
></span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Avatar;
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
interface AvatarTextProps {
|
|
||||||
name: string;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AvatarText: React.FC<AvatarTextProps> = ({ name, className = "" }) => {
|
|
||||||
// Generate initials from name
|
|
||||||
const initials = name
|
|
||||||
.split(" ")
|
|
||||||
.map((word) => word[0])
|
|
||||||
.join("")
|
|
||||||
.toUpperCase()
|
|
||||||
.slice(0, 2);
|
|
||||||
|
|
||||||
// Generate a consistent pastel color based on the name
|
|
||||||
const getColorClass = (name: string) => {
|
|
||||||
const colors = [
|
|
||||||
"bg-brand-100 text-brand-600",
|
|
||||||
"bg-pink-100 text-pink-600",
|
|
||||||
"bg-cyan-100 text-cyan-600",
|
|
||||||
"bg-orange-100 text-orange-600",
|
|
||||||
"bg-green-100 text-green-600",
|
|
||||||
"bg-purple-100 text-purple-600",
|
|
||||||
"bg-yellow-100 text-yellow-600",
|
|
||||||
"bg-error-100 text-error-600",
|
|
||||||
];
|
|
||||||
|
|
||||||
const index = name
|
|
||||||
.split("")
|
|
||||||
.reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
|
||||||
return colors[index % colors.length];
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`flex h-10 w-10 ${className} items-center justify-center rounded-full ${getColorClass(
|
|
||||||
name
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
<span className="text-sm font-medium">{initials}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AvatarText;
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
type BadgeVariant = "light" | "solid";
|
|
||||||
type BadgeSize = "sm" | "md";
|
|
||||||
type BadgeColor =
|
|
||||||
| "primary"
|
|
||||||
| "success"
|
|
||||||
| "error"
|
|
||||||
| "warning"
|
|
||||||
| "info"
|
|
||||||
| "light"
|
|
||||||
| "dark";
|
|
||||||
|
|
||||||
interface BadgeProps {
|
|
||||||
variant?: BadgeVariant; // Light or solid variant
|
|
||||||
size?: BadgeSize; // Badge size
|
|
||||||
color?: BadgeColor; // Badge color
|
|
||||||
startIcon?: React.ReactNode; // Icon at the start
|
|
||||||
endIcon?: React.ReactNode; // Icon at the end
|
|
||||||
children: React.ReactNode; // Badge content
|
|
||||||
}
|
|
||||||
|
|
||||||
const Badge: React.FC<BadgeProps> = ({
|
|
||||||
variant = "light",
|
|
||||||
color = "primary",
|
|
||||||
size = "md",
|
|
||||||
startIcon,
|
|
||||||
endIcon,
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
const baseStyles =
|
|
||||||
"inline-flex items-center px-2.5 py-0.5 justify-center gap-1 rounded-full font-medium";
|
|
||||||
|
|
||||||
// Define size styles
|
|
||||||
const sizeStyles = {
|
|
||||||
sm: "text-theme-xs", // Smaller padding and font size
|
|
||||||
md: "text-sm", // Default padding and font size
|
|
||||||
};
|
|
||||||
|
|
||||||
// Define color styles for variants
|
|
||||||
const variants = {
|
|
||||||
light: {
|
|
||||||
primary:
|
|
||||||
"bg-brand-50 text-brand-500 dark:bg-brand-500/15 dark:text-brand-400",
|
|
||||||
success:
|
|
||||||
"bg-success-50 text-success-600 dark:bg-success-500/15 dark:text-success-500",
|
|
||||||
error:
|
|
||||||
"bg-error-50 text-error-600 dark:bg-error-500/15 dark:text-error-500",
|
|
||||||
warning:
|
|
||||||
"bg-warning-50 text-warning-600 dark:bg-warning-500/15 dark:text-orange-400",
|
|
||||||
info: "bg-blue-light-50 text-blue-light-500 dark:bg-blue-light-500/15 dark:text-blue-light-500",
|
|
||||||
light: "bg-gray-100 text-gray-700 dark:bg-white/5 dark:text-white/80",
|
|
||||||
dark: "bg-gray-500 text-white dark:bg-white/5 dark:text-white",
|
|
||||||
},
|
|
||||||
solid: {
|
|
||||||
primary: "bg-brand-500 text-white dark:text-white",
|
|
||||||
success: "bg-success-500 text-white dark:text-white",
|
|
||||||
error: "bg-error-500 text-white dark:text-white",
|
|
||||||
warning: "bg-warning-500 text-white dark:text-white",
|
|
||||||
info: "bg-blue-light-500 text-white dark:text-white",
|
|
||||||
light: "bg-gray-400 dark:bg-white/5 text-white dark:text-white/80",
|
|
||||||
dark: "bg-gray-700 text-white dark:text-white",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get styles based on size and color variant
|
|
||||||
const sizeClass = sizeStyles[size];
|
|
||||||
const colorStyles = variants[variant][color];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<span className={`${baseStyles} ${sizeClass} ${colorStyles}`}>
|
|
||||||
{startIcon && <span className="mr-1">{startIcon}</span>}
|
|
||||||
{children}
|
|
||||||
{endIcon && <span className="ml-1">{endIcon}</span>}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Badge;
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
import React, { ReactNode } from "react";
|
|
||||||
|
|
||||||
interface ButtonProps {
|
|
||||||
children: ReactNode; // Button text or content
|
|
||||||
size?: "sm" | "md"; // Button size
|
|
||||||
variant?: "primary" | "outline"; // Button variant
|
|
||||||
startIcon?: ReactNode; // Icon before the text
|
|
||||||
endIcon?: ReactNode; // Icon after the text
|
|
||||||
onClick?: () => void; // Click handler
|
|
||||||
disabled?: boolean; // Disabled state
|
|
||||||
className?: string; // Disabled state
|
|
||||||
}
|
|
||||||
|
|
||||||
const Button: React.FC<ButtonProps> = ({
|
|
||||||
children,
|
|
||||||
size = "md",
|
|
||||||
variant = "primary",
|
|
||||||
startIcon,
|
|
||||||
endIcon,
|
|
||||||
onClick,
|
|
||||||
className = "",
|
|
||||||
disabled = false,
|
|
||||||
}) => {
|
|
||||||
// Size Classes
|
|
||||||
const sizeClasses = {
|
|
||||||
sm: "px-4 py-3 text-sm",
|
|
||||||
md: "px-5 py-3.5 text-sm",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Variant Classes
|
|
||||||
const variantClasses = {
|
|
||||||
primary:
|
|
||||||
"bg-brand-500 text-white shadow-theme-xs hover:bg-brand-600 disabled:bg-brand-300",
|
|
||||||
outline:
|
|
||||||
"bg-white text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 dark:bg-gray-800 dark:text-gray-400 dark:ring-gray-700 dark:hover:bg-white/[0.03] dark:hover:text-gray-300",
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
className={`inline-flex items-center justify-center gap-2 rounded-lg transition ${className} ${
|
|
||||||
sizeClasses[size]
|
|
||||||
} ${variantClasses[variant]} ${
|
|
||||||
disabled ? "cursor-not-allowed opacity-50" : ""
|
|
||||||
}`}
|
|
||||||
onClick={onClick}
|
|
||||||
disabled={disabled}
|
|
||||||
>
|
|
||||||
{startIcon && <span className="flex items-center">{startIcon}</span>}
|
|
||||||
{children}
|
|
||||||
{endIcon && <span className="flex items-center">{endIcon}</span>}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Button;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import type React from "react";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
interface DropdownProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
children: React.ReactNode;
|
|
||||||
className?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Dropdown: React.FC<DropdownProps> = ({
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
children,
|
|
||||||
className = "",
|
|
||||||
}) => {
|
|
||||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleClickOutside = (event: MouseEvent) => {
|
|
||||||
if (
|
|
||||||
dropdownRef.current &&
|
|
||||||
!dropdownRef.current.contains(event.target as Node)
|
|
||||||
) {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener("mousedown", handleClickOutside);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("mousedown", handleClickOutside);
|
|
||||||
};
|
|
||||||
}, [onClose]);
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={dropdownRef}
|
|
||||||
className={`absolute z-40 right-0 mt-2 rounded-xl border border-gray-200 bg-white shadow-theme-lg dark:border-gray-800 dark:bg-gray-dark ${className}`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import type React from "react";
|
|
||||||
import { Link } from "react-router";
|
|
||||||
|
|
||||||
interface DropdownItemProps {
|
|
||||||
tag?: "a" | "button";
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
onItemClick?: () => void;
|
|
||||||
baseClassName?: string;
|
|
||||||
className?: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DropdownItem: React.FC<DropdownItemProps> = ({
|
|
||||||
tag = "button",
|
|
||||||
href,
|
|
||||||
onClick,
|
|
||||||
onItemClick,
|
|
||||||
baseClassName = "block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 hover:text-gray-900",
|
|
||||||
className = "",
|
|
||||||
children,
|
|
||||||
}) => {
|
|
||||||
const combinedClasses = `${baseClassName} ${className}`.trim();
|
|
||||||
|
|
||||||
const handleClick = (event: React.MouseEvent) => {
|
|
||||||
if (onClick) onClick();
|
|
||||||
if (onItemClick) onItemClick();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (tag === "a" && href) {
|
|
||||||
return (
|
|
||||||
<Link to={href} className={combinedClasses} onClick={handleClick}>
|
|
||||||
{children}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button onClick={handleClick} className={combinedClasses}>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function ResponsiveImage() {
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<div className="overflow-hidden">
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-01.png"
|
|
||||||
alt="Cover"
|
|
||||||
className="w-full border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function ThreeColumnImageGrid() {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2 xl:grid-cols-3">
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-04.png"
|
|
||||||
alt=" grid"
|
|
||||||
className="border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-05.png"
|
|
||||||
alt=" grid"
|
|
||||||
className="border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-06.png"
|
|
||||||
alt=" grid"
|
|
||||||
className="border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function TwoColumnImageGrid() {
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-02.png"
|
|
||||||
alt=" grid"
|
|
||||||
className="border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<img
|
|
||||||
src="/images/grid-image/image-03.png"
|
|
||||||
alt=" grid"
|
|
||||||
className="border border-gray-200 rounded-xl dark:border-gray-800"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import React, { useRef, useEffect } from "react";
|
|
||||||
|
|
||||||
interface ModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
className?: string;
|
|
||||||
children: React.ReactNode;
|
|
||||||
showCloseButton?: boolean; // New prop to control close button visibility
|
|
||||||
isFullscreen?: boolean; // Default to false for backwards compatibility
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Modal: React.FC<ModalProps> = ({
|
|
||||||
isOpen,
|
|
||||||
onClose,
|
|
||||||
children,
|
|
||||||
className,
|
|
||||||
showCloseButton = true, // Default to true for backwards compatibility
|
|
||||||
isFullscreen = false,
|
|
||||||
}) => {
|
|
||||||
const modalRef = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleEscape = (event: KeyboardEvent) => {
|
|
||||||
if (event.key === "Escape") {
|
|
||||||
onClose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isOpen) {
|
|
||||||
document.addEventListener("keydown", handleEscape);
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener("keydown", handleEscape);
|
|
||||||
};
|
|
||||||
}, [isOpen, onClose]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
document.body.style.overflow = "hidden";
|
|
||||||
} else {
|
|
||||||
document.body.style.overflow = "unset";
|
|
||||||
}
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.body.style.overflow = "unset";
|
|
||||||
};
|
|
||||||
}, [isOpen]);
|
|
||||||
|
|
||||||
if (!isOpen) return null;
|
|
||||||
|
|
||||||
const contentClasses = isFullscreen
|
|
||||||
? "w-full h-full"
|
|
||||||
: "relative w-full rounded-3xl bg-white dark:bg-gray-900";
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fixed inset-0 flex items-center justify-center overflow-y-auto modal z-99999">
|
|
||||||
{!isFullscreen && (
|
|
||||||
<div
|
|
||||||
className="fixed inset-0 h-full w-full bg-gray-400/50 backdrop-blur-[32px]"
|
|
||||||
onClick={onClose}
|
|
||||||
></div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
ref={modalRef}
|
|
||||||
className={`${contentClasses} ${className}`}
|
|
||||||
onClick={(e) => e.stopPropagation()}
|
|
||||||
>
|
|
||||||
{showCloseButton && (
|
|
||||||
<button
|
|
||||||
onClick={onClose}
|
|
||||||
className="absolute right-3 top-3 z-999 flex h-9.5 w-9.5 items-center justify-center rounded-full bg-gray-100 text-gray-400 transition-colors hover:bg-gray-200 hover:text-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white sm:right-6 sm:top-6 sm:h-11 sm:w-11"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
width="24"
|
|
||||||
height="24"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
clipRule="evenodd"
|
|
||||||
d="M6.04289 16.5413C5.65237 16.9318 5.65237 17.565 6.04289 17.9555C6.43342 18.346 7.06658 18.346 7.45711 17.9555L11.9987 13.4139L16.5408 17.956C16.9313 18.3466 17.5645 18.3466 17.955 17.956C18.3455 17.5655 18.3455 16.9323 17.955 16.5418L13.4129 11.9997L17.955 7.4576C18.3455 7.06707 18.3455 6.43391 17.955 6.04338C17.5645 5.65286 16.9313 5.65286 16.5408 6.04338L11.9987 10.5855L7.45711 6.0439C7.06658 5.65338 6.43342 5.65338 6.04289 6.0439C5.65237 6.43442 5.65237 7.06759 6.04289 7.45811L10.5845 11.9997L6.04289 16.5413Z"
|
|
||||||
fill="currentColor"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<div>{children}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
import React, { ReactNode } from "react";
|
|
||||||
|
|
||||||
// Props for Table
|
|
||||||
interface TableProps {
|
|
||||||
children: ReactNode; // Table content (thead, tbody, etc.)
|
|
||||||
className?: string; // Optional className for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Props for TableHeader
|
|
||||||
interface TableHeaderProps {
|
|
||||||
children: ReactNode; // Header row(s)
|
|
||||||
className?: string; // Optional className for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Props for TableBody
|
|
||||||
interface TableBodyProps {
|
|
||||||
children: ReactNode; // Body row(s)
|
|
||||||
className?: string; // Optional className for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Props for TableRow
|
|
||||||
interface TableRowProps {
|
|
||||||
children: ReactNode; // Cells (th or td)
|
|
||||||
className?: string; // Optional className for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Props for TableCell
|
|
||||||
interface TableCellProps {
|
|
||||||
children: ReactNode; // Cell content
|
|
||||||
isHeader?: boolean; // If true, renders as <th>, otherwise <td>
|
|
||||||
className?: string; // Optional className for styling
|
|
||||||
}
|
|
||||||
|
|
||||||
// Table Component
|
|
||||||
const Table: React.FC<TableProps> = ({ children, className }) => {
|
|
||||||
return <table className={`min-w-full ${className}`}>{children}</table>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TableHeader Component
|
|
||||||
const TableHeader: React.FC<TableHeaderProps> = ({ children, className }) => {
|
|
||||||
return <thead className={className}>{children}</thead>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TableBody Component
|
|
||||||
const TableBody: React.FC<TableBodyProps> = ({ children, className }) => {
|
|
||||||
return <tbody className={className}>{children}</tbody>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TableRow Component
|
|
||||||
const TableRow: React.FC<TableRowProps> = ({ children, className }) => {
|
|
||||||
return <tr className={className}>{children}</tr>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// TableCell Component
|
|
||||||
const TableCell: React.FC<TableCellProps> = ({
|
|
||||||
children,
|
|
||||||
isHeader = false,
|
|
||||||
className,
|
|
||||||
}) => {
|
|
||||||
const CellTag = isHeader ? "th" : "td";
|
|
||||||
return <CellTag className={` ${className}`}>{children}</CellTag>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Table, TableHeader, TableBody, TableRow, TableCell };
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
type AspectRatioVideoProps = {
|
|
||||||
videoUrl: string; // URL of the video
|
|
||||||
aspectRatio?: string; // Aspect ratio in the format "width/height", default is "16/9"
|
|
||||||
title?: string; // Video title, default is "Embedded Video"
|
|
||||||
};
|
|
||||||
|
|
||||||
const AspectRatioVideo: React.FC<AspectRatioVideoProps> = ({
|
|
||||||
videoUrl,
|
|
||||||
aspectRatio = "video", // Default aspect ratio
|
|
||||||
title = "Embedded Video",
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div className={`aspect-${aspectRatio} overflow-hidden rounded-lg`}>
|
|
||||||
<iframe
|
|
||||||
src={videoUrl}
|
|
||||||
title={title}
|
|
||||||
frameBorder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowFullScreen
|
|
||||||
className="w-full h-full"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default AspectRatioVideo;
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function FourIsToThree() {
|
|
||||||
return (
|
|
||||||
<div className="aspect-[4/3] overflow-hidden rounded-lg">
|
|
||||||
<iframe
|
|
||||||
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
|
||||||
title="YouTube video"
|
|
||||||
frameBorder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowFullScreen
|
|
||||||
className="w-full h-full"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
export default function OneIsToOne() {
|
|
||||||
return (
|
|
||||||
<div className="overflow-hidden rounded-lg aspect-square">
|
|
||||||
<iframe
|
|
||||||
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
|
|
||||||
title="YouTube video"
|
|
||||||
frameBorder="0"
|
|
||||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
||||||
allowFullScreen
|
|
||||||
className="w-full h-full"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user