Skip to content

Commit

Permalink
minor ui fixes + logic for compiling contracts
Browse files Browse the repository at this point in the history
  • Loading branch information
MCarlomagno committed Dec 6, 2024
1 parent 499d601 commit 7918160
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 102 deletions.
4 changes: 2 additions & 2 deletions src/lib/wizard/components/ApprovalProcess.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
title={disableCreation ? "Deploy Environment already exists" : undefined}
/>
<label
class="text-sm"
class={`text-sm ${disableCreation ? 'text-gray-500' : ''}`}
for="flexRadioDefault2"
title={disableCreation ? "Deploy Environment already exists" : undefined}
>
Expand Down Expand Up @@ -197,7 +197,7 @@
disabled={disableCreation}
/>
<label
class="text-sm"
class={`text-sm ${disableCreation ? 'text-gray-500' : ''}`}
for="flexRadioDefault2"
title={disableCreation ? "Deploy Environment already exists" : undefined}
>
Expand Down
115 changes: 68 additions & 47 deletions src/lib/wizard/components/Configuration.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,77 +5,98 @@
import { globalState } from "$lib/state/state.svelte";
import Button from "./shared/Button.svelte";
import Input from "./shared/Input.svelte";
import Message from "./shared/Message.svelte";
let loading = $state(false);
let successMessage = $state<string | undefined>(undefined);
let errorMessage = $state<string | undefined>(undefined);
let apiKey = $state("");
let apiSecret = $state("");
let apiKey = $state(globalState.credentials?.apiKey ?? "");
let apiSecret = $state(globalState.credentials?.apiSecret ?? "");
function handleGetApiKey() {
// TODO: Implement
window.open(
"https://defender.openzeppelin.com/#/settings/api-keys",
"_blank",
);
}
async function authenticate() {
loading = true;
const result: APIResponse<AuthenticationResponse> = await API.authenticate({ apiKey, apiSecret });
if (result.success) {
globalState.authenticated = true;
successMessage = "API Key Authenticated";
} else {
errorMessage = result.error ?? "Defender Authentication Failed";
}
if (result?.data?.credentials) {
globalState.credentials = {
apiKey: result?.data?.credentials.apiKey,
apiSecret: result?.data?.credentials.apiSecret,
};
}
if (result?.data?.permissions) {
globalState.permissions = result?.data?.permissions;
}
if (result?.data?.networks) {
globalState.networks = result?.data?.networks;
}
if (result?.data?.approvalProcesses) {
globalState.approvalProcesses = result?.data?.approvalProcesses;
}
if (result?.data?.relayers) {
globalState.relayers = result?.data?.relayers;
}
loading = false;
loading = true;
successMessage = undefined;
errorMessage = undefined;
const result: APIResponse<AuthenticationResponse> = await API.authenticate({
apiKey,
apiSecret,
});
if (result.success) {
globalState.authenticated = true;
successMessage = "API Key Authenticated";
} else {
errorMessage = result.error ?? "Defender Authentication Failed";
}
if (result?.data?.credentials) {
globalState.credentials = {
apiKey: result?.data?.credentials.apiKey,
apiSecret: result?.data?.credentials.apiSecret,
};
}
if (result?.data?.permissions) {
globalState.permissions = result?.data?.permissions;
}
if (result?.data?.networks) {
globalState.networks = result?.data?.networks;
}
if (result?.data?.approvalProcesses) {
globalState.approvalProcesses = result?.data?.approvalProcesses;
}
if (result?.data?.relayers) {
globalState.relayers = result?.data?.relayers;
}
loading = false;
}
</script>

<div class="flex flex-col gap-2">
<div class="flex flex-row justify-between">
<div>
<label class="text-xs" for="apiKey">API Key</label>
<i class="fa fa-info-circle text-xs text-gray-500" title="Get your API key from the Defender Dashboard"></i>
<i
class="fa fa-info-circle text-xs text-gray-500"
title="Get your API key from the Defender Dashboard"
></i>
</div>
<button onclick={handleGetApiKey} class="text-xs text-blue-600 font-bold">Get API Key</button>
<button onclick={handleGetApiKey} class="text-xs text-blue-600 font-bold"
>Get API Key</button
>
</div>
<Input value={apiKey} placeholder="Enter your API key" type="text" />
<Input
bind:value={apiKey}
placeholder="Enter your API key"
type="text"
/>

<Input label="Secret" value={apiSecret} placeholder="Enter your API secret" type="password" />
<Input
label="Secret"
bind:value={apiSecret}
placeholder="Enter your API secret"
type="password"
/>

<Button loading={loading} label="Authenticate" onClick={authenticate} />
<Button {loading} label="Authenticate" onClick={authenticate} />

{#if successMessage}
<div class="text-green-600">{successMessage}</div>
<Message message={successMessage} type="success" />
{/if}

{#if errorMessage}
<div class="text-red-600">{errorMessage}</div>
<Message message={errorMessage} type="error" />
{/if}
</div>
5 changes: 3 additions & 2 deletions src/lib/wizard/components/shared/Button.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
loading: boolean;
onClick: () => void;
label: string;
disabled?: boolean;
};
const { loading, onClick, label }: Props = $props();
const { loading, onClick, label, disabled }: Props = $props();
</script>

<button onclick={onClick} disabled={loading} class="bg-blue-600 text-white text-sm rounded-md p-2 mt-2" class:bg-gray-400={loading}>
<button onclick={onClick} disabled={disabled || loading} class="bg-blue-600 text-white text-sm rounded-md p-2 mt-2" class:bg-gray-400={loading}>
{#if loading}
<i class="fa fa-spinner fa-spin"></i>
{/if}
Expand Down
97 changes: 66 additions & 31 deletions src/lib/wizard/components/shared/Dropdown.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@
import type { DropdownItem } from "$lib/models/ui";
import { createEventDispatcher } from "svelte";
function clickOutside(node: HTMLElement, handler: () => void) {
const handleClick = (event: MouseEvent) => {
if (!node.contains(event.target as Node)) {
handler();
}
};
document.addEventListener('click', handleClick, true);
return {
destroy() {
document.removeEventListener('click', handleClick, true);
}
};
}
type Props = {
placeholder: string;
items: DropdownItem[];
Expand Down Expand Up @@ -31,39 +47,58 @@
selected = item;
dispatch("select", item);
};
let isOpen = $state(false);
const toggleDropdown = () => {
if (!disabled) isOpen = !isOpen;
};
const handleSelect = (item: DropdownItem) => {
selected = item;
isOpen = false;
dispatch("select", item);
};
const handleClickOutside = () => {
isOpen = false;
};
</script>

<div class="flex flex-col">
<select name={name} id={name} bind:value={selected} disabled={disabled} class="border border-gray-300 disabled:opacity-50 rounded-md cursor-pointer p-2 text-xs">
<option disabled selected value={undefined}>{placeholder}</option>
{#each Object.entries(groupedItems) as [group, items]}
{#if group !== 'default'}
<optgroup label={group}>
{#each items.sort((a, b) => a.label.localeCompare(b.label)) as item}
<option value={item.value} onclick={() => onSelect(item)}>{item.label}</option>
{/each}
</optgroup>
{:else}
<div class="relative w-full" use:clickOutside={handleClickOutside}>
<button
type="button"
class="w-full flex items-center justify-between border border-gray-300 disabled:opacity-50 rounded-md p-2 text-xs bg-white"
onclick={toggleDropdown}
disabled={disabled}
{name}
>
<span class="truncate">
{selected ? selected.label : placeholder}
</span>
<i class="fa fa-chevron-down text-[8px] transition-transform duration-200 font-light {isOpen ? 'rotate-180' : ''}"></i>
</button>

{#if isOpen}
<div class="absolute z-10 w-full mt-1 bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-auto">
{#each Object.entries(groupedItems) as [group, items]}
{#if group !== 'default'}
<div class="px-2 py-1 text-xs font-semibold bg-gray-50 text-gray-700">{group}</div>
{/if}
{#each items.sort((a, b) => a.label.localeCompare(b.label)) as item}
<option value={item.value} onclick={() => onSelect(item)}>{item.label}</option>
<button
type="button"
class="w-full text-left px-2 py-1.5 text-xs hover:bg-gray-100 focus:bg-gray-100 focus:outline-none {selected?.value === item.value ? 'bg-gray-50' : ''}"
onclick={() => handleSelect(item)}
>
{item.label}
</button>
{/each}
{/each}
{#if items.length === 0}
<div class="px-2 py-1.5 text-xs text-gray-500">
{emptyLabel ?? "No items available"}
</div>
{/if}
{/each}
{#if items.length === 0}
<option disabled>{emptyLabel ?? "No items available"}</option>
{/if}
</select>
</div>


<style>
select {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23000' d='M6 8l-6-6h12l-6 6z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 8px 8px;
}
</style>
</div>
{/if}
</div>
12 changes: 10 additions & 2 deletions src/lib/wizard/components/shared/Input.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
type: 'text' | 'password';
disabled?: boolean;
};
let { label, value, placeholder, type, disabled }: Props = $props();
let { label, value = $bindable(), placeholder, type, disabled }: Props = $props();
</script>

{#if label}
<label class="text-xs" for="apiSecret">{label}</label>
{/if}
<input name="apiSecret" type={type} bind:value={value} placeholder={placeholder} disabled={disabled} class="border text-xs border-gray-300 disabled:opacity-50 rounded-md p-2 w-full" />
<input
name="apiSecret"
type={type}
bind:value={value}
placeholder={placeholder}
disabled={disabled}
class="border text-xs border-gray-300 disabled:opacity-50 rounded-md p-2 w-full"
/>
22 changes: 22 additions & 0 deletions src/lib/wizard/components/shared/Message.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
type Props = {
message: string;
type: 'success' | 'error';
};
let { message, type }: Props = $props();
let color = type === 'success' ? 'green' : 'red';
</script>

{#if type === 'success'}
<div class="flex flex-row items-center gap-2">
<i class={`fa fa-check-circle-o text-green-600`}></i>
<div class="text-xs text-green-600">{message}</div>
</div>
{:else if type === 'error'}
<div class="flex flex-row items-center gap-2">
<i class={`fa fa-times-circle-o text-red-600`}></i>
<div class="text-xs text-red-600">{message}</div>
</div>
{/if}
4 changes: 3 additions & 1 deletion src/lib/wizard/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ function listenToContracts() {
window.addEventListener('message', function (e: MessageEvent<DefenderDeployMessage>) {
if (e.data.kind === 'oz-wizard-defender-deploy') {
globalState.contract = {
source: e.data.sources,
source: {
sources: e.data.sources,
},
target: getMainContractName(e.data.sources)
} ;
}
Expand Down
Loading

0 comments on commit 7918160

Please sign in to comment.