Skip to content

Commit

Permalink
Merge pull request #12 from calimero-network/feat--update-ui
Browse files Browse the repository at this point in the history
feat: update create proposal logic
  • Loading branch information
alenmestrov authored Dec 27, 2024
2 parents bd401e7 + dc99093 commit 96fd691
Show file tree
Hide file tree
Showing 11 changed files with 941 additions and 474 deletions.
5 changes: 5 additions & 0 deletions app/src/api/contractApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ export interface ApprovalsCount {
num_approvals: number;
}

export interface ContextVariables {
key: string;
value: string;
}

export interface ContractApi {
//Contract
getContractProposals(
Expand Down
60 changes: 44 additions & 16 deletions app/src/api/dataSource/ContractApiDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ApiResponse } from '@calimero-is-near/calimero-p2p-sdk';
import {
ApprovalsCount,
ContextDetails,
ContextVariables,
ContractApi,
ContractProposal,
Members,
Expand Down Expand Up @@ -103,23 +104,50 @@ export class ContextApiDataSource implements ContractApi {
}
}

getContextDetails(): ApiResponse<ContextDetails> {
// try {
// const headers: Header | null = await createAuthHeader(
// contextId,
// getNearEnvironment(),
// );
// const response = await this.client.get<ApiContext>(
// `${getAppEndpointKey()}/admin-api/contexts/${contextId}`,
// headers ?? {},
// );
// return response;
// } catch (error) {
// console.error('Error fetching context:', error);
// return { error: { code: 500, message: 'Failed to fetch context data.' } };
// }
throw new Error('Method not implemented.');
async getContextVariables(): ApiResponse<ContextVariables[]> {
try {
const { jwtObject, error } = getConfigAndJwt();
if (error) {
return { error };
}

const apiEndpoint = `${getStorageAppEndpointKey()}/admin-api/contexts/${jwtObject.context_id}/proposals/context-storage-entries`;
const body = {
offset: 0,
limit: 10,
};

const response = await axios.post(apiEndpoint, body, {
headers: {
'Content-Type': 'application/json',
},
});

if (!response.data.data) {
return {
data: [],
error: null,
};
}

// Convert both key and value from Vec<u8> to string
const parsedData = response.data.data.map((item: any) => ({
key: new TextDecoder().decode(new Uint8Array(item.key)),
value: new TextDecoder().decode(new Uint8Array(item.value)),
}));

return {
data: parsedData ?? [],
error: null,
};
} catch (error) {
return {
data: null,
error: error as Error,
};
}
}

getContextMembers(): ApiResponse<Members[]> {
throw new Error('Method not implemented.');
}
Expand Down
185 changes: 185 additions & 0 deletions app/src/components/proposal/Actions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import React from 'react';
import { styled } from 'styled-components';

interface FunctionCallProps {
receiver_id: string;
method_name: string;
args: string;
deposit: number;
gas: number;
}

interface ContextValueProps {
key: number[];
value: number[];
}

interface ApprovalsParams {
num_approvals: number;
}

interface LimitParams {
active_proposals_limit: number;
}

interface TransferParams {
receiver_id: string;
amount: number;
}

interface Action {
scope: string;
params:
| LimitParams
| TransferParams
| ApprovalsParams
| ContextValueProps
| FunctionCallProps;
}

interface ActionsProps {
actions: Action[];
}

interface TableHeaderProps {
columns: number;
bgColor?: boolean;
}

const GridList = styled.div<TableHeaderProps>`
display: grid;
grid-template-columns: repeat(${(props) => props.columns}, 1fr);
gap: 0.5rem;
background: ${(props) => (props.bgColor ? '#ffa500' : 'transparent')};
`;

export default function Actions({ actions }: ActionsProps) {
const getColumnCount = (scope: string) => {
switch (scope) {
case 'Transfer':
case 'SetContextValue':
return 3;
case 'SetActiveProposalsLimit':
case 'SetNumApprovals':
return 2;
case 'ExternalFunctionCall':
return 5;
default:
return 1;
}
};

const renderActionContent = (action: Action) => {
switch (action.scope) {
case 'Transfer':
return (
<>
<div>Scope</div>
<div>Amount</div>
<div>Receiver ID</div>
</>
);
case 'SetContextValue':
return (
<>
<div>Scope</div>
<div>Key</div>
<div>Vaue ID</div>
</>
);
case 'SetActiveProposalsLimit':
return (
<>
<div>Scope</div>
<div>Active Proposals Limit</div>
</>
);
case 'SetNumApprovals':
return (
<>
<div>Scope</div>
<div>Number of Approvals</div>
</>
);
case 'ExternalFunctionCall':
return (
<>
<div>Scope</div>
<div>Receiver ID</div>
<div>Method</div>
<div>Deposit</div>
<div>Gas</div>
</>
);
default:
return <div>Scope</div>;
}
};

const renderActionValues = (action: Action) => {
switch (action.scope) {
case 'Transfer':
const transferParams = action.params as TransferParams;
return (
<>
<div>{action.scope}</div>
<div>{transferParams.amount}</div>
<div>{transferParams.receiver_id}</div>
</>
);
case 'SetContextValue':
const contextValueParams = action.params as ContextValueProps;
return (
<>
<div>{action.scope}</div>
<div>{String.fromCharCode(...contextValueParams.key)}</div>
<div>{String.fromCharCode(...contextValueParams.value)}</div>
</>
);
case 'SetActiveProposalsLimit':
const limitParams = action.params as LimitParams;
return (
<>
<div>{action.scope}</div>
<div>{limitParams.active_proposals_limit}</div>
</>
);
case 'SetNumApprovals':
const approvalParams = action.params as ApprovalsParams;
return (
<>
<div>{action.scope}</div>
<div>{approvalParams.num_approvals}</div>
</>
);
case 'ExternalFunctionCall':
const functionParams = action.params as FunctionCallProps;
return (
<>
<div>{action.scope}</div>
<div>{functionParams.receiver_id}</div>
<div>{functionParams.method_name}</div>
<div>{functionParams.deposit}</div>
<div>{functionParams.gas}</div>
</>
);
default:
return <div>{action.scope}</div>;
}
};

return (
<>
<GridList columns={getColumnCount(actions[0].scope)} bgColor>
{renderActionContent(actions[0])}
</GridList>
<div>
{actions.map((action, index) => (
<GridList key={index} columns={getColumnCount(action.scope)}>
{renderActionValues(action)}
</GridList>
))}
</div>
</>
);
}
62 changes: 62 additions & 0 deletions app/src/components/proposals/ActionsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { FormGroup } from './CreateProposalPopup';

interface ActionsDropdownProps {
actionType: string;
handleInputChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
}

export enum ActionTypes {
CROSS_CONTRACT_CALL = 'Cross contract call',
TRANSFER = 'Transfer',
SET_CONTEXT_VARIABLE = 'Set context variable',
CHANGE_APPROVALS_NEEDED = 'Change number of approvals needed',
CHANGE_MAX_ACTIVE_PROPOSALS = 'Change number of maximum active proposals',
}

export const actionTypes = [
{
id: 'CROSS_CONTRACT_CALL',
label: 'Cross contract call',
},
{
id: 'TRANSFER',
label: 'Transfer',
},
{
id: 'SET_CONTEXT_VARIABLE',
label: 'Set context variable',
},
{
id: 'CHANGE_APPROVALS_NEEDED',
label: 'Change number of approvals needed',
},
{
id: 'CHANGE_MAX_ACTIVE_PROPOSALS',
label: 'Change number of maximum active proposals',
},
];

export default function ActionsDropdown({
actionType,
handleInputChange,
}: ActionsDropdownProps) {
return (
<FormGroup>
<label htmlFor="actionType">Action Type</label>
<select
id="actionType"
name="actionType"
value={actionType}
onChange={handleInputChange}
required
>
{actionTypes.map((action, i) => (
<option key={i} value={action.label}>
{action.label}
</option>
))}
</select>
</FormGroup>
);
}
30 changes: 30 additions & 0 deletions app/src/components/proposals/ChangeApprovalsNeededForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { FormGroup, ProposalData } from './CreateProposalPopup';

interface ChangeApprovalsNeededFormProps {
proposalForm: ProposalData;
handleInputChange: (
e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>,
) => void;
}

export default function ChangeApprovalsNeededForm({
proposalForm,
handleInputChange,
}: ChangeApprovalsNeededFormProps) {
return (
<FormGroup>
<label htmlFor="minApprovals">Minimum Approvals Required</label>
<input
type="number"
id="minApprovals"
name="minApprovals"
placeholder="2"
value={proposalForm.minApprovals}
onChange={handleInputChange}
min="1"
required
/>
</FormGroup>
);
}
Loading

0 comments on commit 96fd691

Please sign in to comment.