org emp multi back intg

This commit is contained in:
janna1702 2024-06-02 22:00:14 +02:00
parent 1082e2fa6c
commit e6611d88fa
11 changed files with 296 additions and 126 deletions

View File

@ -32,6 +32,13 @@ export type PublicKey = {
export type DeployResponse = {
title: string;
owners: PublicKey[];
confirmations: number;
confirmations: string;
};
export type MultiSigResponse = {};
export type InvitationResponse = {
token: string;
token_expired_at: number;
refresh_token: string;
refresh_token_expired_at: number;
};

View File

@ -1,6 +1,7 @@
import { globalService } from "../axios/global-api";
import { AxiosResponse } from "axios";
import Cookies from "js-cookie";
import {
LoginResponse,
OrganizationsResponse,
@ -9,6 +10,7 @@ import {
AddPartResponse,
DeployResponse,
MultiSigResponse,
InvitationResponse,
} from "./api-types";
export class AccountingService {
async login(seedKey: string): Promise<AxiosResponse<LoginResponse>> {
@ -44,10 +46,16 @@ export class AccountingService {
}
async getEmployees(
organizationId: string
organizationId: string,
ids: string[]
): Promise<AxiosResponse<ParticipantsResponse>> {
return await globalService.get(
`organizations/${organizationId}/participants`
return await globalService.post(
`organizations/${organizationId}/participants/fetch`,
{
data: {
ids: [...ids, organizationId],
},
}
);
}
@ -69,16 +77,19 @@ export class AccountingService {
// POST /organizations/{organization_id}/multisig
async deployMultisig(
organization_id: string,
organizationId: string,
title: string,
owners: string[],
confirmations: number
): Promise<AxiosResponse<DeployResponse>> {
return await globalService.post(`${organization_id}/multi-sig`, {
return await globalService.post(
`organizations/${organizationId}/multisig`,
{
title,
owners,
confirmations,
});
}
);
}
async getAllMultisigsByOrganizationId(
@ -86,6 +97,25 @@ export class AccountingService {
): Promise<AxiosResponse<MultiSigResponse>> {
return await globalService.get(`organizations/${organizationId}/multisig`);
}
async sentInvitation(
hash: string,
name: string,
credentals: {
email: string;
phone: string;
telegram: string;
},
mnemonic: string
): Promise<AxiosResponse<InvitationResponse>> {
return await globalService.post(`/invite/${hash}/join`, {
data: {
name,
credentals,
mnemonic,
},
});
}
}
export const apiService = new AccountingService();

View File

@ -14,14 +14,29 @@ export function LoginPage() {
const { passwordVisible, setPasswordVisible } = useLoginHooks();
const [inp, setInp] = useState("");
//
const [seed, setSeed] = useState<string[]>(["airport","donate","language","disagree","dumb","access","insect","tribe","ozone","humor","foot","jealous","much","digital",]);
const [seed, setSeed] = useState<string[]>([
"airport",
"donate",
"language",
"disagree",
"dumb",
"access",
"insect",
"tribe",
"ozone",
"humor",
"foot",
"jealous",
"much",
"digital",
]);
// const [seed, setSeed] = useState<string[]>(["melody","correct","brain","slide","flip","polar","asset","know","pencil","major","smile","vital","nominee","merge","addict"]);
const [disabled, setDisabled] = useState(true);
const router = useRouter();
const onLogin = async () => {
const result = await apiService.login(seed.join(' '));
const result = await apiService.login(seed.join(" "));
// if (result) {
Cookies.set("accessToken", result.data.token);
router.push("/organization");
@ -33,7 +48,7 @@ export function LoginPage() {
Cookies.set("accessToken", result.data.token);
router.push("/organization");
}
}
};
// const getSeed = (event: React.ChangeEvent<HTMLInputElement>) => {
// setSeed(inp);
// };
@ -44,7 +59,7 @@ export function LoginPage() {
}, [inp]);
const onSubmitHandler = async () => {
setSeed((prevState) => prevState ? [...prevState, inp] : [inp]);
setSeed((prevState) => (prevState ? [...prevState, inp] : [inp]));
setInp("");
};
@ -91,14 +106,17 @@ export function LoginPage() {
</Button>
</Space.Compact>
</div>
{ <div
{
<div
className="flex flex-row w-[700px] gap-3 content-box flex-wrap
"
>
{seed && seed.map((element: string, index: number) => (
{seed &&
seed.map((element: string, index: number) => (
<SeedItem key={index} seed={element} />
))}
</div> }
</div>
}
<Button
onClick={onLogin}
style={{ width: "150px" }}

View File

@ -14,7 +14,6 @@ type OrgItemProps = {
export const OrganizationCard: FC<OrgItemProps> = ({ element }) => {
const router = useRouter();
const id: any = element.id;
return (
<>
<Card
@ -22,7 +21,10 @@ export const OrganizationCard: FC<OrgItemProps> = ({ element }) => {
type="inner"
extra={
<Link
href={{ pathname: "/organization/overview/dashboard/", query: id }}
href={{
pathname: "/organization/overview/dashboard/",
query: { id: id },
}}
>
Go To Overview
</Link>

View File

@ -13,6 +13,7 @@ import { apiService } from "../axios/global.service";
import { OrgForm } from "./OrgForm";
import { OrganizationCard } from "./OrgCard";
import { FolderOpenTwoTone } from "@ant-design/icons";
import useOrganizationsHooks from "@/hooks/organizations";
import {
Organization,
NewOrgResponse,
@ -20,7 +21,8 @@ import {
} from "../axios/api-types";
import Cookies from "js-cookie";
export function OrgCreatePage() {
const [organizations, setOrganizations] = useState<Organization[]>([]);
//const [organizations, setOrganizations] = useState<Organization[]>([]);
const { organizations, setOrganizations } = useOrganizationsHooks();
const [isModalOpen, setIsModalOpen] = useState(false);
const [formData, setFormData] = useState({
name: "",
@ -47,16 +49,19 @@ export function OrgCreatePage() {
setIsModalOpen(false);
};
console.log(organizations);
const handleCancel = () => {
setIsModalOpen(false);
};
useEffect(() => {
loadOrganizations();
}, []);
const loadOrganizations = async () => {
const result = await apiService.getOrganizations();
if (result) {
setOrganizations(result.data.items);
setOrganizations(result.data.items || []);
}
};
return (
@ -69,7 +74,7 @@ export function OrgCreatePage() {
</div>
<div></div>
<div className="flex flex-col relative w-full h-3/4 items-center overflow-scroll gap-10 p-10 z-0">
{organizations.length === 0 && (
{organizations.length && (
<FolderOpenTwoTone style={{ fontSize: "400%" }} />
)}
{organizations.length ? (

View File

@ -1,10 +1,17 @@
"use client";
import React, { useState, useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { Card } from "antd";
import { useRouter } from "next/navigation";
import { useSearchParams } from "next/navigation";
import { UserOutlined } from "@ant-design/icons";
import { Button, List, Divider, Typography, Avatar, Skeleton } from "antd";
import { OrganizationCard } from "@/app/orgCreate/OrgCard";
import { Organization } from "@/app/axios/api-types";
import useOrganizationsHooks from "@/hooks/organizations";
import { apiService } from "@/app/axios/global.service";
type OrgItemProps = {
element: Organization;
};
interface DataType {
gender?: string;
name: {
@ -35,53 +42,51 @@ const data = [
const { Title } = Typography;
export function OrgProfile() {
const { organizations, setOrganizations, loadOrganizations } =
useOrganizationsHooks();
const [organization, setOrganization] = useState<Organization>({
id: "",
name: "",
address: "",
});
const [initLoading, setInitLoading] = useState(true);
const [loading, setLoading] = useState(false);
const [dataEmployees, setData] = useState<DataType[]>([]);
const [list, setList] = useState<DataType[]>([]);
const [list, setList] = useState<any[]>([]);
const router = useRouter();
const pathname = useSearchParams();
console.log(pathname.getAll("query"));
const searchParams = useSearchParams();
useEffect(() => {
const id = searchParams.get("id") || "";
const filteredOrganization = organizations.find(
(element) => element.id === id
);
if (filteredOrganization) {
loadEmployees(id);
setOrganization(filteredOrganization);
setInitLoading(false);
}
}, [organizations]);
useEffect(() => {
loadOrganizations();
}, []);
const loadEmployees = async (id: string) => {
const data: any = await apiService.getEmployees(id, []);
setList(data.data.participants);
};
const onNextPageHandler = () => {
router.push("/organization/overview/employees");
};
const onMultisigPageHandler = () => {
router.push("/organization/overview/multiSig");
router.push("/organization/overview/multiSig/?id=" + organization.id);
};
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"));
});
loadEmployees(organization.id);
setInitLoading(false);
};
const loadMore =
!initLoading && !loading ? (
@ -101,7 +106,12 @@ export function OrgProfile() {
<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
<div style={{ width: "60%" }} className="flex flex-col ">
{organization && <OrganizationCard element={organization} />}
</div>
{/* <Card
title="Organization Name"
bordered={false}
style={{ width: "60%" }}
@ -109,7 +119,7 @@ export function OrgProfile() {
<p>Address</p>
<p>Phone</p>
<p>Description</p>
</Card>
</Card> */}
<div className="flex w-full justify-end ">
<Button
type="primary"
@ -161,7 +171,10 @@ export function OrgProfile() {
itemLayout="horizontal"
loadMore={loadMore}
dataSource={list}
renderItem={(item) => (
renderItem={(item) => {
console.log(item);
return (
<List.Item
actions={[
<a key="list-loadmore-edit">edit</a>,
@ -171,13 +184,14 @@ export function OrgProfile() {
<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>}
title={<a href="https://ant.design">{item.name}</a>}
description="1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71"
/>
<div>wallet address</div>
</Skeleton>
</List.Item>
)}
);
}}
/>
</div>
</div>

View File

@ -1,6 +1,7 @@
"use client";
import React from "react";
import { OrgProfile } from "./id";
import { OrgProfile } from "./[id]";
import { useSearchParams } from "next/navigation";
export default function Home() {
return (

View File

@ -6,8 +6,26 @@
import React, { useState, useEffect } from "react";
import { Input, Button, Typography, Space } from "antd";
import { MailOutlined, CopyOutlined } from "@ant-design/icons";
import { apiService } from "@/app/axios/global.service";
export function EmployeeInvitatonPage() {
const { Text } = Typography;
const [values, setValues] = useState({
email: "ower@gmail.com",
phone: "79999999999",
telegram: "@ower",
});
async function sendInvite() {
const data = await apiService.sentInvitation(
"",
"Alex",
values,
"short orient camp maple lend pole balance token pledge fat analyst badge art happy sadsad"
);
console.log(data);
}
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 p-10 text-neutral-500">
<div className="w-full h-20 bg-[#1677FF] absolute top-0 flex items-center justify-center">
@ -16,12 +34,21 @@ export function EmployeeInvitatonPage() {
<div className="flex flex-col w-9/12 pb-36 gap-5 items-start ">
<Text type="secondary">Invite new Employee</Text>
<Space.Compact style={{ width: "100%" }}>
<Input size="large" addonBefore="E-mail" />
<Input
size="large"
name="email"
addonBefore="E-mail"
value={values.email}
onChange={(e) =>
setValues((prev) => ({ ...prev, email: e.target.value }))
}
/>
<Button
size="large"
icon={<MailOutlined />}
style={{ width: 210 }}
type="primary"
onClick={sendInvite}
>
Send Invite
</Button>

View File

@ -4,6 +4,10 @@ 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";
import { apiService } from "@/app/axios/global.service";
import useOrganizationsHooks from "@/hooks/organizations";
import { useSearchParams } from "next/navigation";
import { Organization } from "@/app/axios/api-types";
const count = 8;
const fakeDataUrl = `https://randomuser.me/api/?results=${count}&inc=name,gender,email,nat,picture&noinfo`;
interface DataType {
@ -25,11 +29,35 @@ interface DataType {
const { Title } = Typography;
type MenuItem = Required<MenuProps>["items"][number];
export function EmployeeList() {
const {
organizations,
filteredOrganization,
setOrganizations,
loadOrganizations,
} = useOrganizationsHooks();
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 [organization, setOrganization] = useState<Organization>({
id: "",
name: "",
address: "",
});
const loadEmployees = async (id: string) => {
const data: any = await apiService.getEmployees(id, []);
setList(data.data.participants);
};
const searchParams = useSearchParams();
useEffect(() => {
const id = searchParams.get("id") || "";
if (filteredOrganization) {
loadEmployees(id);
setOrganization(filteredOrganization);
setInitLoading(false);
}
}, [organizations]);
useEffect(() => {
fetch(fakeDataUrl)
.then((res) => res.json())
@ -41,27 +69,7 @@ export function EmployeeList() {
}, []);
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"));
});
loadEmployees(organization.id);
};
const loadMore =
!initLoading && !loading ? (
@ -77,7 +85,7 @@ export function EmployeeList() {
</div>
) : null;
return (
<div className="flex flex-col w-full h-full gap-5 pb-20 px-30 p-8">
<div className="flex flex-col w-full h-full gap-5 pb-20 px-30 p-10">
<Title style={{ color: "#302d43", textIndent: 15 }}>Employee List</Title>
<List
className="demo-loadmore-list"
@ -85,18 +93,22 @@ export function EmployeeList() {
itemLayout="horizontal"
loadMore={loadMore}
dataSource={list}
renderItem={(item) => (
renderItem={(item) => {
console.log(item);
return (
<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>}
title={<a href="https://ant.design">{item.name}</a>}
description="1Lbcfr7sAHTD9CgdQo3HTMTkV8LK4ZnX71"
/>
<div>wallet address</div>
</Skeleton>
</List.Item>
)}
);
}}
/>
</div>
);

View File

@ -6,14 +6,37 @@ import { Card } from "antd";
import { WalletOutlined } from "@ant-design/icons";
import type { InputNumberProps } from "antd";
import { Col, InputNumber, Row, Slider, Select, Button } from "antd";
import { apiService } from "@/app/axios/global.service";
import Cookies from "js-cookie";
import { useRouter, useSearchParams } from "next/navigation";
const { Title } = Typography;
export function MultisigPage() {
const searchParams = useSearchParams();
const [multiSigData, setmultiSigData] = useState({
title: "",
owners: [],
confirmations: 0,
organizationId: searchParams.get("id") || "",
});
const router = useRouter();
const [inputValue, setInputValue] = useState(1);
const onChange: InputNumberProps["onChange"] = (newValue) => {
setInputValue(newValue as number);
};
const onDeployMultisig = async () => {
const result = await apiService.deployMultisig(
multiSigData.organizationId,
multiSigData.title,
multiSigData.owners,
multiSigData.confirmations
);
if (result) {
//Cookies.set("multisig", {result.data.confirmations, result.data.owners, result.data.organizationId, result.data.title});
}
};
return (
<div className="flex flex-col w-full h-full p-8 gap-10 ">
<div className="flex flex-col w-1/3">
@ -93,7 +116,7 @@ export function MultisigPage() {
</Card>
</div>
<div className="flex w-full justify-end pr-5">
<Button size={"large"} type="primary">
<Button onClick={onDeployMultisig} size={"large"} type="primary">
Create Multisig
</Button>
</div>

View File

@ -0,0 +1,31 @@
import { useState } from "react";
import { Organization } from "@/app/axios/api-types";
import { apiService } from "@/app/axios/global.service";
import { useSearchParams } from "next/navigation";
export default function useOrganizationsHooks() {
const [organizations, setOrganizations] = useState<Organization[]>([]);
const [filteredOrganization, setFilteredOrganization] = useState<
Organization[]
>([]);
const loadOrganizations = async () => {
const result = await apiService.getOrganizations();
if (result) {
setOrganizations(result.data.items || []);
}
const searchParams = useSearchParams();
const id = searchParams.get("id") || "";
const filteredOrg = organizations.find((element) => element.id === id);
if (filteredOrg) {
setFilteredOrganization(filteredOrg || []);
}
};
return {
organizations,
filteredOrganization,
setOrganizations,
loadOrganizations,
};
}