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": {
"@ant-design/icons": "^5.3.7",
"antd": "^5.17.0",
"axios": "^1.7.2",
"js-cookie": "^3.0.5",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
@ -649,6 +652,12 @@
"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": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@ -1165,6 +1174,11 @@
"integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
"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": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@ -1189,6 +1203,16 @@
"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": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@ -1385,6 +1409,17 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"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": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
@ -1563,6 +1598,14 @@
"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": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@ -2330,6 +2373,25 @@
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
"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": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@ -2355,6 +2417,19 @@
"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": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
@ -3155,6 +3230,14 @@
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -3343,6 +3426,25 @@
"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": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@ -3971,6 +4073,11 @@
"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": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",

View File

@ -11,11 +11,14 @@
"dependencies": {
"@ant-design/icons": "^5.3.7",
"antd": "^5.17.0",
"axios": "^1.7.2",
"js-cookie": "^3.0.5",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/js-cookie": "^3.0.6",
"@types/node": "^20",
"@types/react": "^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";
import React from "react";
import { SeedItem } from "../seedItem/SeedItem";
import { useRouter } from "next/navigation";
import { useState, useEffect, FC } from "react";
import { EyeInvisibleOutlined, EyeTwoTone } from "@ant-design/icons";
import { apiService } from "../axios/global.service";
import { Input, Space, Button, List, Card, Typography } from "antd";
import { LockOutlined } from "@ant-design/icons";
import useLoginHooks from "@/hooks/login";
import Cookies from "js-cookie";
export function LoginPage() {
const { passwordVisible, setPasswordVisible, seed, getSeed } =
useLoginHooks();
const { passwordVisible, setPasswordVisible } = useLoginHooks();
const [inp, setInp] = useState("");
const [seed, setSeed] = useState<string>();
const [disabled, setDisabled] = useState(true);
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;
useEffect(() => {
setDisabled(!(inp.length >= 4));
}, [inp]);
const onSubmitHandler = () => {
const onSubmitHandler = async () => {
const result = await apiService.login(inp);
if (result) {
Cookies.set("accessToken", result.data.token);
router.push("/organization");
}
setInp("");
getSeed(inp);
};
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="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center">
@ -39,7 +43,9 @@ export function LoginPage() {
</div>
<div className="flex flex-col w-6/12 gap-3 items-start mt-20">
<div>
<Text type="secondary">Please enter 12 words always lowercase.</Text>
<Text type="secondary">
Please enter by spaces 12 words always lowercase.
</Text>
</div>
<Space.Compact style={{ width: "100%" }}>
@ -59,8 +65,9 @@ export function LoginPage() {
}
placeholder="Enter your seed words in order"
onInput={(event: any) => setInp(event.target.value)}
maxLength={8}
//maxLength={8}
minLength={4}
//onChange={getSeed}
/>
<Button
size="large"
@ -72,14 +79,14 @@ export function LoginPage() {
</Button>
</Space.Compact>
</div>
<div
{/* <div
className="flex flex-row w-[700px] gap-3 content-box flex-wrap
"
>
{seed.map((element: string, index: number) => (
<SeedItem key={index} seed={element} />
))}
</div>
{/* {seed.map((element: string, index: number) => (
// <SeedItem key={index} seed={element} />
))}
</div> */}
<Button
onClick={onNextPageHandler}
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 { useState, useEffect, FC } from "react";
import { useRouter } from "next/navigation";
type OrgData = {
name: string;
address: string;
phone: number;
};
import Link from "next/link";
import { Organization } from "../axios/api-types";
type OrgItemProps = {
element: OrgData;
element: Organization;
};
export const OrganizationCard: FC<OrgItemProps> = ({ element }) => {
const router = useRouter();
const onNextPageHandler = () => {
router.push("/organization/dashboard");
};
const id: any = element.id;
return (
<>
<Card
title={element.name}
type="inner"
extra={
<a onClick={onNextPageHandler} href="#">
<Link
href={{ pathname: "/organization/overview/dashboard/", query: id }}
>
More
</a>
</Link>
}
>
<p>{element.address}</p>
<p>{element.phone}</p>
<p>{element.name}</p>
</Card>
</>
);

View File

@ -6,35 +6,39 @@
//* <h1>{seed.join("\n")}</h1> */shtuchka kak map
"use client";
import React from "react";
import React, { useEffect } from "react";
import { Button, Modal } from "antd";
import { useState } from "react";
import { apiService } from "../axios/global.service";
import { OrgForm } from "./OrgForm";
import { OrganizationCard } from "./OrgCard";
import { FolderOpenTwoTone } from "@ant-design/icons";
type OrgData = {
name: string;
address: string;
phone: number;
};
import {
Organization,
NewOrgResponse,
OrganizationsResponse,
} from "../axios/api-types";
import Cookies from "js-cookie";
export function OrgCreatePage() {
const [organizations, setOrganizations] = useState([
{
name: "My Company",
address: "2930 Pearl St Boulder, CO 80301 United States",
phone: "+1303-245-0086",
},
]);
const [organizations, setOrganizations] = useState<Organization[]>([]);
const [isModalOpen, setIsModalOpen] = useState(false);
const onFinish = (values: any) => {
const [formData, setFormData] = useState({
name: "",
address: "",
});
const onFinish = async (values: any) => {
handleOk();
setOrganizations((prev: any[]) => [...prev, formData]);
setFormData({});
setOrganizations((prev: any) => [...prev, formData]);
// setFormData({});
const result = await apiService.newOrganization(
formData.name,
formData.address
);
if (result) {
loadOrganizations();
}
};
const [formData, setFormData] = useState({});
const showModal = () => {
setIsModalOpen(true);
};
@ -46,7 +50,15 @@ export function OrgCreatePage() {
const handleCancel = () => {
setIsModalOpen(false);
};
useEffect(() => {
loadOrganizations();
}, []);
const loadOrganizations = async () => {
const result: Organization[] = await apiService.getOrganizations();
if (result) {
setOrganizations(result.data.items);
}
};
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">

View File

@ -39,10 +39,10 @@ export function OrgForm({ setFormData }) {
<Input
name="name"
style={{ width: 350 }}
onInput={(e: any) =>
onInput={(element: any) =>
setFormData((prev: object) => ({
...prev,
[e.target.name]: e.target.value,
[element.target.name]: element.target.value,
}))
}
/>
@ -58,10 +58,10 @@ export function OrgForm({ setFormData }) {
>
<Input
name="address"
onInput={(e: any) =>
onInput={(element: any) =>
setFormData((prev: object) => ({
...prev,
[e.target.name]: e.target.value,
[element.target.name]: element.target.value,
}))
}
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";
import React, { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { useRouter, useSearchParams } from "next/navigation";
import { Card } from "antd";
import {
AppstoreOutlined,
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";
import { UserOutlined } from "@ant-design/icons";
import { Button, List, Divider, Typography, Avatar, Skeleton } from "antd";
interface DataType {
gender?: string;
name: {
@ -41,7 +24,6 @@ interface DataType {
const count = 3;
const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`;
type MenuItem = Required<MenuProps>["items"][number];
const data = [
"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",
@ -50,51 +32,23 @@ const data = [
"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;
export function OrgProfile() {
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[]>([]);
const router = useRouter();
const pathname = useSearchParams();
console.log(pathname.getAll("query"));
const onNextPageHandler = () => {
router.push("/organization/employees");
router.push("/organization/overview/employees");
};
const onMultisigPageHandler = () => {
router.push("/organization/multiSig");
router.push("/organization/overview/multiSig");
};
useEffect(() => {
fetch(fakeDataUrl)
@ -144,25 +98,8 @@ export function OrgProfile() {
) : null;
return (
<div className="flex flex-row w-full h-full bg-slate-50 gap-5 pb-20 px-30 p-10">
<div className="w-24 py-2">
<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">
<div className="flex flex-row w-full h-full bg-slate-50 p-8">
<div className="flex flex-col w-11/12 ">
<Title style={{ color: "#302d43", textIndent: 15 }}>Dashboard</Title>
<Card
title="Organization Name"
@ -188,7 +125,7 @@ export function OrgProfile() {
orientation="left"
orientationMargin="0"
>
Recent Activities
<a href="#">Contracts</a>
</Divider>
<List
bordered
@ -214,7 +151,9 @@ export function OrgProfile() {
orientation="left"
orientationMargin="0"
>
Employee List
<a href="http://localhost:3000/organization/employees/employeeList">
Employee List
</a>
</Divider>
<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 { Input, Button, Typography, Space } from "antd";
import { MailOutlined, CopyOutlined } from "@ant-design/icons";
export function EmployeePage() {
export function EmployeeInvitatonPage() {
const { Text } = Typography;
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">
<h1 className="text-white text-xl font-semibold">Invite</h1>
</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";
import React from "react";
import { EmployeePage } from "./Employee";
import { EmployeeInvitatonPage } from "./Employee";
export default function Home() {
return (
<div className="flex w-full h-screen items-center justify-center bg-white">
<EmployeePage />
<EmployeeInvitatonPage />
</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 { Typography } from "antd";
import { Card } from "antd";
import { WalletOutlined } from "@ant-design/icons";
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;
@ -14,14 +15,14 @@ export function MultisigPage() {
setInputValue(newValue as number);
};
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">
<Title level={3}>Create a new Multisig</Title>
<Title>Create a new Multisig</Title>
<Input size="large" placeholder="Multisig Name/Label" />
</div>
<div className="flex w-full ">
<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-col gap-2 w-1/4">
<Input placeholder="Name" />
@ -29,14 +30,32 @@ export function MultisigPage() {
<Input placeholder="Name" />
</div>
<div className="flex flex-col gap-2 w-full">
<Input placeholder="Pubic Key" />
<Input placeholder="Pubic Key" />
<Input placeholder="Pubic Key" />
<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>
</div>
<div className="flex w-full justify-end mt-5">
<Button size={"large"} type="primary">
Add Signer
Add Owner
</Button>
</div>
</Card>
@ -53,7 +72,7 @@ export function MultisigPage() {
<Col span={12}>
<Slider
min={1}
max={20}
max={5}
onChange={onChange}
value={typeof inputValue === "number" ? inputValue : 0}
/>
@ -61,7 +80,7 @@ export function MultisigPage() {
<Col span={4}>
<InputNumber
min={1}
max={20}
max={5}
style={{ margin: "0 16px" }}
value={inputValue}
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";
import React from "react";
import { OrgCreatePage } from "./OrgCreatePage";
// вместо этого надо класть большие компоненты
import { OrgCreatePage } from "../orgCreate/OrgCreatePage";
export default function organization() {
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() {
const [passwordVisible, setPasswordVisible] = useState([]);
const [seed, setSeed] = useState<string[]>([]);
const getSeed = (string: string) => {
if (seed.length < 12) {
setSeed((prev) => [...prev, string]);
}
};
console.log(seed);
// const getSeed = (string: string) => {
// // if (seed.length < 12) {
// setSeed((prev) => [...prev, string]);
// };
return { passwordVisible, setPasswordVisible, seed, getSeed };
return { passwordVisible, setPasswordVisible };
}