back integ

Co-authored-by: Nick <emochka2007@users.noreply.github.com>
This commit is contained in:
janna1702 2024-05-31 14:42:52 +02:00
parent 8791e4f103
commit f01dbe094b
30 changed files with 913 additions and 170 deletions

107
front/package-lock.json generated
View File

@ -10,11 +10,14 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"antd": "^5.17.0", "antd": "^5.17.0",
"axios": "^1.7.2",
"js-cookie": "^3.0.5",
"next": "14.2.3", "next": "14.2.3",
"react": "^18", "react": "^18",
"react-dom": "^18" "react-dom": "^18"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",
@ -649,6 +652,12 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==",
"dev": true
},
"node_modules/@types/json5": { "node_modules/@types/json5": {
"version": "0.0.29", "version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -1165,6 +1174,11 @@
"integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
"dev": true "dev": true
}, },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/available-typed-arrays": { "node_modules/available-typed-arrays": {
"version": "1.0.7", "version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@ -1189,6 +1203,16 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/axios": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
"integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": { "node_modules/axobject-query": {
"version": "3.2.1", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@ -1385,6 +1409,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true "dev": true
}, },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": { "node_modules/commander": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -1563,6 +1598,14 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dequal": { "node_modules/dequal": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -2330,6 +2373,25 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"dev": true "dev": true
}, },
"node_modules/follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/for-each": { "node_modules/for-each": {
"version": "0.3.3", "version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -2355,6 +2417,19 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3155,6 +3230,14 @@
"jiti": "bin/jiti.js" "jiti": "bin/jiti.js"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -3343,6 +3426,25 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3971,6 +4073,11 @@
"react-is": "^16.13.1" "react-is": "^16.13.1"
} }
}, },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"node_modules/punycode": { "node_modules/punycode": {
"version": "2.3.1", "version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -11,11 +11,14 @@
"dependencies": { "dependencies": {
"@ant-design/icons": "^5.3.7", "@ant-design/icons": "^5.3.7",
"antd": "^5.17.0", "antd": "^5.17.0",
"axios": "^1.7.2",
"js-cookie": "^3.0.5",
"next": "14.2.3", "next": "14.2.3",
"react": "^18", "react": "^18",
"react-dom": "^18" "react-dom": "^18"
}, },
"devDependencies": { "devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^18",
"@types/react-dom": "^18", "@types/react-dom": "^18",

View File

@ -0,0 +1,37 @@
export type LoginResponse = {
token: string;
token_expired_at: number;
refresh_token: string;
refresh_token_expired_at: number;
};
export type Organization = {
id: string;
name: string;
address: string;
};
export type OrganizationsResponse = {
items: Organization[];
};
export type NewOrgResponse = {
id: string;
};
export type Ids = {
id: string;
};
export type ParticipantsResponse = {
ids: Ids[];
};
export type AddPartResponse = {
name: string;
position: string;
wallet_address: string;
};
export type PublicKey = {
public_key: string;
};
export type DeployResponse = {
title: string;
owners: PublicKey[];
confirmations: number;
};
export type MultiSigResponse = {};

View File

@ -0,0 +1,27 @@
import axios from "axios";
import Cookies from "js-cookie";
// const tokenStorage = Cookies.get("accessToken");
export const globalService = axios.create({
baseURL: "http://209.141.36.222:8085/",
timeout: 10000,
});
globalService.interceptors.request.use(
(config) => {
// Get the access token from the cookie
const token = Cookies.get("accessToken");
console.log(token);
// If the token exists, set it in the Authorization header
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
// If there's an error in the request configuration, you can handle it here
return Promise.reject(error);
}
);

View File

@ -0,0 +1,91 @@
import { globalService } from "../axios/global-api";
import { AxiosResponse } from "axios";
import Cookies from "js-cookie";
import {
LoginResponse,
OrganizationsResponse,
NewOrgResponse,
ParticipantsResponse,
AddPartResponse,
DeployResponse,
MultiSigResponse,
} from "./api-types";
export class AccountingService {
async login(seedKey: string): Promise<AxiosResponse<LoginResponse>> {
console.log(seedKey);
return await globalService.post("login", {
mnemonic: seedKey,
});
}
async register(seedKey: string) {
return await globalService.post("join", {
mnemonic: seedKey,
});
}
async newOrganization(
name: string,
address: string
): Promise<AxiosResponse<NewOrgResponse>> {
return await globalService.post("organizations", {
name,
address,
});
}
async getOrganizations(): Promise<AxiosResponse<OrganizationsResponse>> {
return await globalService.post("organizations/fetch", {
data: {
limit: 5,
},
});
}
async getEmployees(
organizationId: string
): Promise<AxiosResponse<ParticipantsResponse>> {
return await globalService.get(
`organizations/${organizationId}/participants`
);
}
async addEmployee(
name: string,
position: string,
wallet_address: string,
organizationId: string
): Promise<AxiosResponse<AddPartResponse>> {
return await globalService.post(
`organizations/${organizationId}/participants`,
{
name,
position,
wallet_address,
}
);
}
// POST /organizations/{organization_id}/multisig
async deployMultisig(
organization_id: string,
title: string,
owners: string[],
confirmations: number
): Promise<AxiosResponse<DeployResponse>> {
return await globalService.post(`${organization_id}/multi-sig`, {
title,
owners,
confirmations,
});
}
async getAllMultisigsByOrganizationId(
organizationId: string
): Promise<AxiosResponse<MultiSigResponse>> {
return await globalService.get(`organizations/${organizationId}/multisig`);
}
}
export const apiService = new AccountingService();

View File

@ -1,37 +1,41 @@
// 1. Страница входа (Login Page)
// Дизайн: Страница должна быть минималистичной с использованием профессиональной цветовой схемы (синие и серые тона). Должно быть поле для ввода мнемонической фразы (SEED_KEY) и кнопка для входа.
// Безопасность: Добавить элементы, подчеркивающие безопасность, например, иконку замка и текст, имитирующий шифрование.
// Валидация: Проверять формат введенного SEED_KEY на клиентской стороне перед отправкой на сервер.
"use client"; "use client";
import React from "react"; import React from "react";
import { SeedItem } from "../seedItem/SeedItem"; import { SeedItem } from "../seedItem/SeedItem";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useState, useEffect, FC } from "react"; import { useState, useEffect, FC } from "react";
import { EyeInvisibleOutlined, EyeTwoTone } from "@ant-design/icons"; import { EyeInvisibleOutlined, EyeTwoTone } from "@ant-design/icons";
import { apiService } from "../axios/global.service";
import { Input, Space, Button, List, Card, Typography } from "antd"; import { Input, Space, Button, List, Card, Typography } from "antd";
import { LockOutlined } from "@ant-design/icons"; import { LockOutlined } from "@ant-design/icons";
import useLoginHooks from "@/hooks/login"; import useLoginHooks from "@/hooks/login";
import Cookies from "js-cookie";
export function LoginPage() { export function LoginPage() {
const { passwordVisible, setPasswordVisible, seed, getSeed } = const { passwordVisible, setPasswordVisible } = useLoginHooks();
useLoginHooks();
const [inp, setInp] = useState(""); const [inp, setInp] = useState("");
const [seed, setSeed] = useState<string>();
const [disabled, setDisabled] = useState(true); const [disabled, setDisabled] = useState(true);
const router = useRouter(); const router = useRouter();
const onNextPageHandler = () => {
router.push("/organization"); const onNextPageHandler = async () => {};
}; // const getSeed = (event: React.ChangeEvent<HTMLInputElement>) => {
// setSeed(inp);
// };
console.log(inp);
const { Text } = Typography; const { Text } = Typography;
useEffect(() => { useEffect(() => {
setDisabled(!(inp.length >= 4)); setDisabled(!(inp.length >= 4));
}, [inp]); }, [inp]);
const onSubmitHandler = () => { const onSubmitHandler = async () => {
const result = await apiService.login(inp);
if (result) {
Cookies.set("accessToken", result.data.token);
router.push("/organization");
}
setInp(""); setInp("");
getSeed(inp);
}; };
return ( return (
<div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center gap-10 bg-white border-solid border rounded-md border-neutral-300 text-neutral-500"> <div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center gap-10 bg-white border-solid border rounded-md border-neutral-300 text-neutral-500">
<div className="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center"> <div className="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center">
@ -39,7 +43,9 @@ export function LoginPage() {
</div> </div>
<div className="flex flex-col w-6/12 gap-3 items-start mt-20"> <div className="flex flex-col w-6/12 gap-3 items-start mt-20">
<div> <div>
<Text type="secondary">Please enter 12 words always lowercase.</Text> <Text type="secondary">
Please enter by spaces 12 words always lowercase.
</Text>
</div> </div>
<Space.Compact style={{ width: "100%" }}> <Space.Compact style={{ width: "100%" }}>
@ -59,8 +65,9 @@ export function LoginPage() {
} }
placeholder="Enter your seed words in order" placeholder="Enter your seed words in order"
onInput={(event: any) => setInp(event.target.value)} onInput={(event: any) => setInp(event.target.value)}
maxLength={8} //maxLength={8}
minLength={4} minLength={4}
//onChange={getSeed}
/> />
<Button <Button
size="large" size="large"
@ -72,14 +79,14 @@ export function LoginPage() {
</Button> </Button>
</Space.Compact> </Space.Compact>
</div> </div>
<div {/* <div
className="flex flex-row w-[700px] gap-3 content-box flex-wrap className="flex flex-row w-[700px] gap-3 content-box flex-wrap
" "
> >
{seed.map((element: string, index: number) => ( {/* {seed.map((element: string, index: number) => (
<SeedItem key={index} seed={element} /> // <SeedItem key={index} seed={element} />
))} ))}
</div> </div> */}
<Button <Button
onClick={onNextPageHandler} onClick={onNextPageHandler}
style={{ width: "150px" }} style={{ width: "150px" }}

View File

@ -1,5 +0,0 @@
export class LoginService {
login(seedKey: string) {
return fetch(`backend`);
}
}

View File

@ -4,33 +4,32 @@ import React from "react";
import { Card } from "antd"; import { Card } from "antd";
import { useState, useEffect, FC } from "react"; import { useState, useEffect, FC } from "react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
type OrgData = { import Link from "next/link";
name: string; import { Organization } from "../axios/api-types";
address: string;
phone: number;
};
type OrgItemProps = { type OrgItemProps = {
element: OrgData; element: Organization;
}; };
export const OrganizationCard: FC<OrgItemProps> = ({ element }) => { export const OrganizationCard: FC<OrgItemProps> = ({ element }) => {
const router = useRouter(); const router = useRouter();
const onNextPageHandler = () => { const id: any = element.id;
router.push("/organization/dashboard");
};
return ( return (
<> <>
<Card <Card
title={element.name} title={element.name}
type="inner" type="inner"
extra={ extra={
<a onClick={onNextPageHandler} href="#"> <Link
href={{ pathname: "/organization/overview/dashboard/", query: id }}
>
More More
</a> </Link>
} }
> >
<p>{element.address}</p> <p>{element.address}</p>
<p>{element.phone}</p> <p>{element.name}</p>
</Card> </Card>
</> </>
); );

View File

@ -6,35 +6,39 @@
//* <h1>{seed.join("\n")}</h1> */shtuchka kak map //* <h1>{seed.join("\n")}</h1> */shtuchka kak map
"use client"; "use client";
import React from "react"; import React, { useEffect } from "react";
import { Button, Modal } from "antd"; import { Button, Modal } from "antd";
import { useState } from "react"; import { useState } from "react";
import { apiService } from "../axios/global.service";
import { OrgForm } from "./OrgForm"; import { OrgForm } from "./OrgForm";
import { OrganizationCard } from "./OrgCard"; import { OrganizationCard } from "./OrgCard";
import { FolderOpenTwoTone } from "@ant-design/icons"; import { FolderOpenTwoTone } from "@ant-design/icons";
import {
type OrgData = { Organization,
name: string; NewOrgResponse,
address: string; OrganizationsResponse,
phone: number; } from "../axios/api-types";
}; import Cookies from "js-cookie";
export function OrgCreatePage() { export function OrgCreatePage() {
const [organizations, setOrganizations] = useState([ const [organizations, setOrganizations] = useState<Organization[]>([]);
{
name: "My Company",
address: "2930 Pearl St Boulder, CO 80301 United States",
phone: "+1303-245-0086",
},
]);
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [formData, setFormData] = useState({
const onFinish = (values: any) => { name: "",
address: "",
});
const onFinish = async (values: any) => {
handleOk(); handleOk();
setOrganizations((prev: any[]) => [...prev, formData]); setOrganizations((prev: any) => [...prev, formData]);
setFormData({});
// setFormData({});
const result = await apiService.newOrganization(
formData.name,
formData.address
);
if (result) {
loadOrganizations();
}
}; };
const [formData, setFormData] = useState({});
const showModal = () => { const showModal = () => {
setIsModalOpen(true); setIsModalOpen(true);
}; };
@ -46,7 +50,15 @@ export function OrgCreatePage() {
const handleCancel = () => { const handleCancel = () => {
setIsModalOpen(false); setIsModalOpen(false);
}; };
useEffect(() => {
loadOrganizations();
}, []);
const loadOrganizations = async () => {
const result: Organization[] = await apiService.getOrganizations();
if (result) {
setOrganizations(result.data.items);
}
};
return ( return (
<> <>
<div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center z-30 gap-5 bg-white border-solid border rounded-md border-neutral-300 text-neutral-500"> <div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center z-30 gap-5 bg-white border-solid border rounded-md border-neutral-300 text-neutral-500">

View File

@ -39,10 +39,10 @@ export function OrgForm({ setFormData }) {
<Input <Input
name="name" name="name"
style={{ width: 350 }} style={{ width: 350 }}
onInput={(e: any) => onInput={(element: any) =>
setFormData((prev: object) => ({ setFormData((prev: object) => ({
...prev, ...prev,
[e.target.name]: e.target.value, [element.target.name]: element.target.value,
})) }))
} }
/> />
@ -58,10 +58,10 @@ export function OrgForm({ setFormData }) {
> >
<Input <Input
name="address" name="address"
onInput={(e: any) => onInput={(element: any) =>
setFormData((prev: object) => ({ setFormData((prev: object) => ({
...prev, ...prev,
[e.target.name]: e.target.value, [element.target.name]: element.target.value,
})) }))
} }
style={{ width: 350 }} style={{ width: 350 }}

View File

@ -1,7 +0,0 @@
"use client";
import React from "react";
import { OrgProfile } from "./OrgProfile";
export default function Home() {
return <OrgProfile />;
}

View File

@ -0,0 +1,69 @@
"use client";
import React, { useState, useEffect } from "react";
import { Select, Typography, Card, Divider, Button } from "antd";
import { WalletOutlined } from "@ant-design/icons";
const handleChange = (value: string) => {
console.log(`selected ${value}`);
};
const { Title } = Typography;
export function AgreementPage() {
return (
<div className="flex flex-col w-full p-8 ">
<Title style={{ color: "#302d43" }}>Agreement</Title>
<Card className="w-full ">
<Divider
style={{ color: "#1677FF" }}
orientation="left"
orientationMargin="0"
>
<a href="#">Owners</a>
</Divider>
<div className="flex flex-col gap-2">
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
</div>
</Card>
<div className="flex w-full justify-end mt-8">
<Button size={"large"} style={{ width: 150 }} type="primary">
Confirm
</Button>
</div>
</div>
);
}

View File

@ -0,0 +1,11 @@
"use client";
import React from "react";
import { AgreementPage } from "./AgreementPage";
export default function Home() {
return (
<div className="flex w-full h-screen bg-slate-50">
<AgreementPage />
</div>
);
}

View File

@ -1,27 +1,10 @@
"use client"; "use client";
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useRouter } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import { Card } from "antd"; import { Card } from "antd";
import { import { UserOutlined } from "@ant-design/icons";
AppstoreOutlined, import { Button, List, Divider, Typography, Avatar, Skeleton } from "antd";
ContainerOutlined,
DesktopOutlined,
MailOutlined,
MenuFoldOutlined,
MenuUnfoldOutlined,
PieChartOutlined,
UserOutlined,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import {
Button,
Menu,
List,
Divider,
Typography,
Avatar,
Skeleton,
} from "antd";
interface DataType { interface DataType {
gender?: string; gender?: string;
name: { name: {
@ -41,7 +24,6 @@ interface DataType {
const count = 3; const count = 3;
const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`; const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`;
type MenuItem = Required<MenuProps>["items"][number];
const data = [ const data = [
"Ackee Blockchain is a team of auditors and white hat hackers who perform security audits and assessments for Ethereum and Solana.", "Ackee Blockchain is a team of auditors and white hat hackers who perform security audits and assessments for Ethereum and Solana.",
"Global blockchain services company and Initial Coin Offering solutions provider", "Global blockchain services company and Initial Coin Offering solutions provider",
@ -50,51 +32,23 @@ const data = [
"Securing the DeFi ecosystem", "Securing the DeFi ecosystem",
]; ];
const items: MenuItem[] = [
{ key: "1", icon: <PieChartOutlined />, label: "Wallet Info" },
{ key: "2", icon: <DesktopOutlined />, label: "Option 2" },
{ key: "3", icon: <ContainerOutlined />, label: "Contracts" },
{
key: "sub1",
label: "Navigation One",
icon: <MailOutlined />,
// children: [
// { key: "5", label: "Option 5" },
// { key: "6", label: "Option 6" },
// ],
},
{
key: "sub2",
label: "Navigation Two",
icon: <AppstoreOutlined />,
children: [
{ key: "9", label: "Option 9" },
{ key: "10", label: "Option 10" },
{
key: "sub3",
label: "Submenu",
children: [
{ key: "11", label: "Option 11" },
{ key: "12", label: "Option 12" },
],
},
],
},
];
const { Title } = Typography; const { Title } = Typography;
export function OrgProfile() { export function OrgProfile() {
const [collapsed, setCollapsed] = useState(true);
const [initLoading, setInitLoading] = useState(true); const [initLoading, setInitLoading] = useState(true);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [dataEmployees, setData] = useState<DataType[]>([]); const [dataEmployees, setData] = useState<DataType[]>([]);
const [list, setList] = useState<DataType[]>([]); const [list, setList] = useState<DataType[]>([]);
const router = useRouter(); const router = useRouter();
const pathname = useSearchParams();
console.log(pathname.getAll("query"));
const onNextPageHandler = () => { const onNextPageHandler = () => {
router.push("/organization/employees"); router.push("/organization/overview/employees");
}; };
const onMultisigPageHandler = () => { const onMultisigPageHandler = () => {
router.push("/organization/multiSig"); router.push("/organization/overview/multiSig");
}; };
useEffect(() => { useEffect(() => {
fetch(fakeDataUrl) fetch(fakeDataUrl)
@ -144,25 +98,8 @@ export function OrgProfile() {
) : null; ) : null;
return ( return (
<div className="flex flex-row w-full h-full bg-slate-50 gap-5 pb-20 px-30 p-10"> <div className="flex flex-row w-full h-full bg-slate-50 p-8">
<div className="w-24 py-2"> <div className="flex flex-col w-11/12 ">
<div>
<Menu
style={{
borderRadius: 8,
height: "228px",
border: "solid 1px #1677FF",
}}
defaultSelectedKeys={["1"]}
defaultOpenKeys={["sub1"]}
mode="inline"
theme="light"
inlineCollapsed={collapsed}
items={items}
/>
</div>
</div>
<div className="flex flex-col w-11/12 pr-10">
<Title style={{ color: "#302d43", textIndent: 15 }}>Dashboard</Title> <Title style={{ color: "#302d43", textIndent: 15 }}>Dashboard</Title>
<Card <Card
title="Organization Name" title="Organization Name"
@ -188,7 +125,7 @@ export function OrgProfile() {
orientation="left" orientation="left"
orientationMargin="0" orientationMargin="0"
> >
Recent Activities <a href="#">Contracts</a>
</Divider> </Divider>
<List <List
bordered bordered
@ -214,7 +151,9 @@ export function OrgProfile() {
orientation="left" orientation="left"
orientationMargin="0" orientationMargin="0"
> >
Employee List <a href="http://localhost:3000/organization/employees/employeeList">
Employee List
</a>
</Divider> </Divider>
<List <List
className="demo-loadmore-list" className="demo-loadmore-list"

View File

@ -0,0 +1,11 @@
"use client";
import React from "react";
import { OrgProfile } from "./id";
export default function Home() {
return (
<div className="flex flex-row w-full h-screen bg-slate-50 gap-5">
<OrgProfile />
</div>
);
}

View File

@ -6,10 +6,10 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { Input, Button, Typography, Space } from "antd"; import { Input, Button, Typography, Space } from "antd";
import { MailOutlined, CopyOutlined } from "@ant-design/icons"; import { MailOutlined, CopyOutlined } from "@ant-design/icons";
export function EmployeePage() { export function EmployeeInvitatonPage() {
const { Text } = Typography; const { Text } = Typography;
return ( return (
<div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center gap-10 bg-white border-solid border rounded-md border-neutral-300 text-neutral-500"> <div className="flex relative overflow-hidden flex-col w-2/3 h-3/4 items-center justify-center gap-10 bg-white border-solid border rounded-md border-neutral-300 p-10 text-neutral-500">
<div className="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center"> <div className="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center">
<h1 className="text-white text-xl font-semibold">Invite</h1> <h1 className="text-white text-xl font-semibold">Invite</h1>
</div> </div>

View File

@ -0,0 +1,103 @@
"use client";
import React, { useState, useEffect } from "react";
import { Button, Menu, List, Typography, Avatar, Skeleton } from "antd";
import { UserOutlined } from "@ant-design/icons";
import type { MenuProps } from "antd";
import { PayOutBtn } from "@/app/ui/PayOutBtn";
const count = 8;
const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`;
interface DataType {
gender?: string;
name: {
title?: string;
first?: string;
last?: string;
};
email?: string;
picture: {
large?: string;
medium?: string;
thumbnail?: string;
};
nat?: string;
loading: boolean;
}
const { Title } = Typography;
type MenuItem = Required<MenuProps>["items"][number];
export function EmployeeList() {
const [collapsed, setCollapsed] = useState(true);
const [initLoading, setInitLoading] = useState(true);
const [loading, setLoading] = useState(false);
const [dataEmployees, setData] = useState<DataType[]>([]);
const [list, setList] = useState<DataType[]>([]);
useEffect(() => {
fetch(fakeDataUrl)
.then((res) => res.json())
.then((res) => {
setInitLoading(false);
setData(res.results);
setList(res.results);
});
}, []);
const onLoadMore = () => {
setLoading(true);
setList(
dataEmployees.concat(
[...new Array(count)].map(() => ({
loading: true,
name: {},
picture: {},
}))
)
);
fetch(fakeDataUrl)
.then((res) => res.json())
.then((res) => {
const newData = dataEmployees.concat(res.results);
setData(newData);
setList(newData);
setLoading(false);
// Resetting window's offsetTop so as to display react-virtualized demo underfloor.
// In real scene, you can using public method of react-virtualized:
// https://stackoverflow.com/questions/46700726/how-to-use-public-method-updateposition-of-react-virtualized
window.dispatchEvent(new Event("resize"));
});
};
const loadMore =
!initLoading && !loading ? (
<div
style={{
textAlign: "center",
marginTop: 12,
height: 32,
lineHeight: "32px",
}}
>
<Button onClick={onLoadMore}>loading more</Button>
</div>
) : null;
return (
<div className="flex flex-col w-full h-full gap-5 pb-20 px-30 p-8">
<Title style={{ color: "#302d43", textIndent: 15 }}>Employee List</Title>
<List
className="demo-loadmore-list"
loading={initLoading}
itemLayout="horizontal"
loadMore={loadMore}
dataSource={list}
renderItem={(item) => (
<List.Item actions={[<PayOutBtn />]}>
<Skeleton avatar title={false} loading={item.loading} active>
<List.Item.Meta
avatar={<Avatar icon={<UserOutlined />} />}
title={<a href="https://ant.design">{item.name?.last}</a>}
description="1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71"
/>
<div>wallet address</div>
</Skeleton>
</List.Item>
)}
/>
</div>
);
}

View File

@ -0,0 +1,11 @@
"use client";
import React from "react";
import { EmployeeList } from "./EmployeeList";
export default function Home() {
return (
<div className="flex w-full h-screen items-center justify-center bg-slate-50">
<EmployeeList />
</div>
);
}

View File

@ -1,11 +1,11 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { EmployeePage } from "./Employee"; import { EmployeeInvitatonPage } from "./Employee";
export default function Home() { export default function Home() {
return ( return (
<div className="flex w-full h-screen items-center justify-center bg-white"> <div className="flex w-full h-screen items-center justify-center bg-white">
<EmployeePage /> <EmployeeInvitatonPage />
</div> </div>
); );
} }

View File

@ -0,0 +1,117 @@
"use client";
import React, { useState } from "react";
import Link from "next/link";
import { BackBtn } from "@/app/ui/BackBtn";
import {
PieChartOutlined,
KeyOutlined,
UsergroupAddOutlined,
FileProtectOutlined,
WalletOutlined,
} from "@ant-design/icons";
import type { MenuProps } from "antd";
import { Menu } from "antd";
export default function Layout({ children }: { children: React.ReactNode }) {
type MenuItem = Required<MenuProps>["items"][number];
const [collapsed, setCollapsed] = useState(true);
const items: MenuItem[] = [
{
key: "1",
icon: <PieChartOutlined />,
label: (
<Link href="http://localhost:3000/organization/overview/dashboard">
Overview
</Link>
),
},
{
key: "2",
icon: <FileProtectOutlined />,
children: [
{
key: "5",
label: (
<Link href="http://localhost:3000/organization/overview/license">
Licenses
</Link>
),
},
{
key: "6",
label: (
<Link href="http://localhost:3000/organization/overview/agreement">
Agreement
</Link>
),
},
],
},
{
key: "3",
icon: <WalletOutlined />,
label: "Transactions",
children: [
{
key: "5",
label: (
<Link href="http://localhost:3000/organization/overview/pending">
Pending Contracts
</Link>
),
},
{ key: "6", label: "Option 6" },
],
},
{
key: "4",
icon: <UsergroupAddOutlined />,
label: (
<Link href="http://localhost:3000/organization//overview/employees/employeeList">
Employees
</Link>
),
},
{
key: "sub1",
label: (
<Link href="http://localhost:3000/organization/overview/multiSig">
Multisig
</Link>
),
icon: <KeyOutlined />,
// children: [
// { key: "5", label: "Option 5" },
// { key: "6", label: "Option 6" },
// ],
},
];
return (
<div className="flex h-screen flex-col md:flex-row md:overflow-hidden justify-center pl-10 bg-slate-50">
<div className="w-full flex-none md:w-24 pt-24">
<Menu
style={{
borderRadius: 8,
height: "228px",
border: "solid 1px #1677FF",
}}
defaultSelectedKeys={["1"]}
defaultOpenKeys={["sub1"]}
mode="inline"
theme="light"
inlineCollapsed={collapsed}
items={items}
/>
</div>
<div className="flex-grow md:overflow-y-auto pt-2">
<div className="flex justify-end pt-2 mr-2">
<BackBtn />
</div>
{children}
</div>
</div>
);
}

View File

@ -0,0 +1,49 @@
"use client";
import React, { useState, useEffect } from "react";
import { Typography, Button, Card, Input } from "antd";
import { WalletOutlined } from "@ant-design/icons";
const { Title } = Typography;
const data = [
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
];
export function LicensesPage() {
return (
<div className="flex flex-col w-full p-8 ">
<Title style={{ color: "#302d43" }}>Licenses</Title>
<Card style={{ width: "100%" }}>
<div className=" flex flex-row w-full gap-10">
<div className="flex flex-col gap-2 w-1/4">
<Title level={4}>Owners</Title>
<Input placeholder="Name" />
<Input placeholder="Name" />
<Input placeholder="Name" />
<Input placeholder="Name" />
<Input placeholder="Name" />
</div>
<div className="flex flex-col gap-2 w-full">
<Title level={4}>Shares</Title>
<Input placeholder="Input information" />
<Input placeholder="Input information" />
<Input placeholder="Input information" />
<Input placeholder="Input information" />
<Input placeholder="Input information" />
</div>
</div>
</Card>
<div className="flex w-full justify-end mt-5">
<Button size={"large"} style={{ width: "150px" }} type="primary">
Confirm
</Button>
</div>
</div>
);
}
//suffixIcon={<WalletOutlined />}

View File

@ -0,0 +1,11 @@
"use client";
import React from "react";
import { LicensesPage } from "./LicensesPage";
export default function Home() {
return (
<div className="flex w-full h-screen bg-slate-50">
<LicensesPage />
</div>
);
}

View File

@ -3,8 +3,9 @@ import React, { useState } from "react";
import { Input } from "antd"; import { Input } from "antd";
import { Typography } from "antd"; import { Typography } from "antd";
import { Card } from "antd"; import { Card } from "antd";
import { WalletOutlined } from "@ant-design/icons";
import type { InputNumberProps } from "antd"; import type { InputNumberProps } from "antd";
import { Col, InputNumber, Row, Slider, Space, Button } from "antd"; import { Col, InputNumber, Row, Slider, Select, Button } from "antd";
const { Title } = Typography; const { Title } = Typography;
@ -14,14 +15,14 @@ export function MultisigPage() {
setInputValue(newValue as number); setInputValue(newValue as number);
}; };
return ( return (
<div className="flex flex-col w-full h-full px-28 py-20 gap-10 "> <div className="flex flex-col w-full h-full p-8 gap-10 ">
<div className="flex flex-col w-1/3"> <div className="flex flex-col w-1/3">
<Title level={3}>Create a new Multisig</Title> <Title>Create a new Multisig</Title>
<Input size="large" placeholder="Multisig Name/Label" /> <Input size="large" placeholder="Multisig Name/Label" />
</div> </div>
<div className="flex w-full "> <div className="flex w-full ">
<Card style={{ width: "100%" }}> <Card style={{ width: "100%" }}>
<Title level={4}>Signers</Title> <Title level={4}>Owners</Title>
<div className="flex flex-row gap-5"> <div className="flex flex-row gap-5">
<div className="flex flex-col gap-2 w-1/4"> <div className="flex flex-col gap-2 w-1/4">
<Input placeholder="Name" /> <Input placeholder="Name" />
@ -29,14 +30,32 @@ export function MultisigPage() {
<Input placeholder="Name" /> <Input placeholder="Name" />
</div> </div>
<div className="flex flex-col gap-2 w-full"> <div className="flex flex-col gap-2 w-full">
<Input placeholder="Pubic Key" /> <Select
<Input placeholder="Pubic Key" /> suffixIcon={<WalletOutlined />}
<Input placeholder="Pubic Key" /> defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
<Select
suffixIcon={<WalletOutlined />}
defaultValue={""}
style={{ width: "full" }}
allowClear
options={[{ value: "", label: "" }]}
/>
</div> </div>
</div> </div>
<div className="flex w-full justify-end mt-5"> <div className="flex w-full justify-end mt-5">
<Button size={"large"} type="primary"> <Button size={"large"} type="primary">
Add Signer Add Owner
</Button> </Button>
</div> </div>
</Card> </Card>
@ -53,7 +72,7 @@ export function MultisigPage() {
<Col span={12}> <Col span={12}>
<Slider <Slider
min={1} min={1}
max={20} max={5}
onChange={onChange} onChange={onChange}
value={typeof inputValue === "number" ? inputValue : 0} value={typeof inputValue === "number" ? inputValue : 0}
/> />
@ -61,7 +80,7 @@ export function MultisigPage() {
<Col span={4}> <Col span={4}>
<InputNumber <InputNumber
min={1} min={1}
max={20} max={5}
style={{ margin: "0 16px" }} style={{ margin: "0 16px" }}
value={inputValue} value={inputValue}
onChange={onChange} onChange={onChange}

View File

@ -0,0 +1,91 @@
"use client";
import React, { useState, useEffect } from "react";
import { Steps, Typography, Card, Input, Space, Button, Select } from "antd";
import type { MenuProps } from "antd";
import { WalletOutlined } from "@ant-design/icons";
const { Title } = Typography;
type MenuItem = Required<MenuProps>["items"][number];
const data = [
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
"1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71",
];
export function PendingPage() {
return (
<div className="flex flex-col w-full h-full gap-5 p-8 ">
<Title style={{ color: "#302d43" }}>Pending Confirmations</Title>
<Steps
current={1}
items={[
{
title: "Request",
},
{
title: "Verify",
},
{
title: "Success",
},
]}
/>
<Card className="flex flex-col w-full ">
<Space.Compact style={{ width: "100%" }}>
<Input
size="middle"
defaultValue=""
placeholder="Pay Out Transaction"
/>
<Button style={{ color: "#4096ff", borderColor: "#4096ff" }}>
Submit
</Button>
</Space.Compact>
<div className="flex justify-end mt-5">
<Button type="primary" style={{ width: "150px" }}>
Execute
</Button>
</div>
</Card>
<Card className="flex flex-col w-full ">
<Space.Compact style={{ width: "100%" }}>
<Input
size="middle"
defaultValue=""
placeholder="Pay Out Transaction"
/>
<Button style={{ color: "#4096ff", borderColor: "#4096ff" }}>
Submit
</Button>
</Space.Compact>
<div className="flex justify-end mt-5">
<Button type="primary" style={{ width: "150px" }}>
Execute
</Button>
</div>
</Card>
<Card className="flex flex-col w-full ">
<Space.Compact style={{ width: "100%" }}>
<Input
size="middle"
defaultValue=""
placeholder="Pay Out Transaction"
/>
<Button style={{ color: "#4096ff", borderColor: "#4096ff" }}>
Submit
</Button>
</Space.Compact>
<div className="flex justify-end mt-5">
<Button type="primary" style={{ width: "150px" }}>
Execute
</Button>
</div>
</Card>
</div>
);
}

View File

@ -0,0 +1,11 @@
"use client";
import React from "react";
import { PendingPage } from "./PendingPage";
export default function Home() {
return (
<div className="flex w-full h-screen items-center justify-center bg-slate-50">
<PendingPage />
</div>
);
}

View File

@ -1,7 +1,6 @@
"use client"; "use client";
import React from "react"; import React from "react";
import { OrgCreatePage } from "./OrgCreatePage"; import { OrgCreatePage } from "../orgCreate/OrgCreatePage";
// вместо этого надо класть большие компоненты
export default function organization() { export default function organization() {
return ( return (

View File

@ -0,0 +1,20 @@
"use client";
import { Button } from "antd";
import { useRouter } from "next/navigation";
import { ArrowLeftOutlined } from "@ant-design/icons";
export function BackBtn() {
const router = useRouter();
const goBack = () => {
router.back();
};
return (
<Button
className="flex items-center"
type="text"
size="large"
onClick={goBack}
>
<ArrowLeftOutlined />
</Button>
);
}

View File

@ -0,0 +1,14 @@
"use client";
import { Button } from "antd";
import { useRouter } from "next/navigation";
export function PayOutBtn() {
const router = useRouter();
const onPayHandler = () => {
router.push("/organization/overview/pending");
};
return (
<Button onClick={onPayHandler} type="primary">
Pay Out
</Button>
);
}

View File

@ -2,14 +2,11 @@ import { useState } from "react";
export default function useLoginHooks() { export default function useLoginHooks() {
const [passwordVisible, setPasswordVisible] = useState([]); const [passwordVisible, setPasswordVisible] = useState([]);
const [seed, setSeed] = useState<string[]>([]);
const getSeed = (string: string) => { // const getSeed = (string: string) => {
if (seed.length < 12) { // // if (seed.length < 12) {
setSeed((prev) => [...prev, string]); // setSeed((prev) => [...prev, string]);
} // };
};
console.log(seed);
return { passwordVisible, setPasswordVisible, seed, getSeed }; return { passwordVisible, setPasswordVisible };
} }