Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/frontend/src/lib/components/auth/AuthSettings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import AuthConfigII from '$lib/components/auth/AuthConfigII.svelte';
import AuthProviders from '$lib/components/auth/AuthProviders.svelte';
import AuthSettingsLoader from '$lib/components/auth/AuthSettingsLoader.svelte';
import { listCustomDomains } from '$lib/services/satellite/custom-domain.services';
import { listCustomDomains } from '$lib/services/satellite/hosting/custom-domain.services';
import { busy } from '$lib/stores/app/busy.store';
import { initAuthConfigContext } from '$lib/stores/satellite/auth.context.store';
import { AUTH_CONFIG_CONTEXT_KEY, type AuthConfigContext } from '$lib/types/auth.context';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import SpinnerParagraph from '$lib/components/ui/SpinnerParagraph.svelte';
import Warning from '$lib/components/ui/Warning.svelte';
import { authIdentity } from '$lib/derived/auth.derived';
import { getAuthConfig } from '$lib/services/satellite/auth.config.services';
import { getRuleUser } from '$lib/services/satellite/collection.services';
import { getAuthConfig } from '$lib/services/satellite/authentication/auth.config.services';
import { getRuleUser } from '$lib/services/satellite/collection/collection.services';
import { i18n } from '$lib/stores/app/i18n.store';
import { versionStore } from '$lib/stores/version.store';
import { AUTH_CONFIG_CONTEXT_KEY, type AuthConfigContext } from '$lib/types/auth.context';
Expand Down
6 changes: 6 additions & 0 deletions src/frontend/src/lib/components/automation/Automation.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<script lang="ts">
import { isNullish } from '@dfinity/utils';
import { setContext } from 'svelte';
import { fade } from 'svelte/transition';
import AutomationConfigLoader from '$lib/components/automation/AutomationConfigLoader.svelte';
import AutomationNew from '$lib/components/automation/AutomationNew.svelte';
import NoAutomation from '$lib/components/automation/NoAutomation.svelte';
import WorkflowsContext from '$lib/components/automation/workflows/WorkflowsContext.svelte';
import { initAutomationConfigContext } from '$lib/stores/satellite/automation.context.store';
import {
AUTOMATION_CONFIG_CONTEXT_KEY,
Expand All @@ -27,5 +29,9 @@
<NoAutomation />

<AutomationNew {satellite} />
{:else}
<div in:fade>
<WorkflowsContext {satellite} />
</div>
{/if}
</AutomationConfigLoader>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import SpinnerParagraph from '$lib/components/ui/SpinnerParagraph.svelte';
import Warning from '$lib/components/ui/Warning.svelte';
import { authIdentity } from '$lib/derived/auth.derived';
import { getAutomationConfig } from '$lib/services/satellite/automation.config.services';
import { getAutomationConfig } from '$lib/services/satellite/automation/automation.config.services';
import { i18n } from '$lib/stores/app/i18n.store';
import { versionStore } from '$lib/stores/version.store';
import {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { SatelliteDid } from '$declarations';
import { buildRepositoryKey } from '$lib/services/satellite/automation.config.services';
import { buildRepositoryKey } from '$lib/services/satellite/automation/automation.config.services';
import { i18n } from '$lib/stores/app/i18n.store';

interface Props {
Expand Down
100 changes: 100 additions & 0 deletions src/frontend/src/lib/components/automation/workflows/Workflow.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<script lang="ts">
import { notEmptyString } from '@dfinity/utils';
import { i18n } from '$lib/stores/app/i18n.store';
import type { Workflow, WorkflowKey } from '$lib/types/workflow';
import { toRepositoryKey, toRunId } from '$lib/utils/workflow.utils';

interface Props {
key: WorkflowKey;
workflow: Workflow;
}

let { key, workflow }: Props = $props();

let { workflow: workflowName, runNumber, sha, eventName, actor } = $derived(workflow.data);

let repoKey = $derived(toRepositoryKey(key));
let runId = $derived(toRunId(key));

let runIdHref = $derived(
`https://github.com/${repoKey.owner}/${repoKey.name}/actions/runs/${runId ?? ''}`
);

let shaHref = $derived(`https://github.com/${repoKey.owner}/${repoKey.name}/commit/${sha ?? ''}`);
</script>

{#snippet actorLink({ actor }: { actor: string })}
<a
aria-label={$i18n.automation.view_contributor}
href={`https://github.com/${actor}`}
rel="noopener noreferrer"
target="_blank">{actor}</a
>
{/snippet}

{#snippet workflowDispatch(params: { actor: string })}
<span>{$i18n.automation.manually_run_by} {@render actorLink(params)}</span>
{/snippet}

{#snippet pullRequest(params: { actor: string })}
<span>{$i18n.automation.pr_run_by} {@render actorLink(params)}</span>
{/snippet}

{#snippet push({ actor, sha }: { actor: string; sha: string })}
<span
>{$i18n.automation.commit}
<a
aria-label={$i18n.automation.view_commit}
href={shaHref}
rel="noopener noreferrer"
target="_blank">{sha.slice(0, 7) ?? ''}</a
>
{$i18n.automation.pushed_by}
{@render actorLink({ actor })}</span
>
{/snippet}

{#if notEmptyString(workflowName) || notEmptyString(actor)}
<div>
{#if notEmptyString(workflowName)}
<p class="workflow">
{workflowName}
{#if notEmptyString(runNumber)}(<a
aria-label={$i18n.automation.view_workflow}
href={runIdHref}
rel="noopener noreferrer"
target="_blank">#{runNumber}</a
>){/if}
</p>
{/if}

{#if notEmptyString(actor)}
<p class="event">
{#if eventName === 'workflow_dispatch'}
{@render workflowDispatch({ actor })}
{:else if eventName === 'pull_request'}
{@render pullRequest({ actor })}
{:else if notEmptyString(sha)}
{@render push({ actor, sha })}
{/if}
</p>
{/if}
</div>
{:else}
<p>{$i18n.automation.no_workflow_info}</p>
{/if}

<style lang="scss">
.workflow {
margin: 0 0 var(--padding-0_25x);

a {
text-decoration: none;
}
}

.event {
font-size: var(--font-size-small);
color: var(--value-color);
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import ListParamsFilter from '$lib/components/list/ListParamsFilter.svelte';
import { i18n } from '$lib/stores/app/i18n.store';
</script>

<ListParamsFilter
direction="ltr"
key={{ label: $i18n.sort.keys, placeholder: $i18n.automation.workflow_placeholder }}
ownerFilter={false}
/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<script lang="ts">
import { notEmptyString } from '@dfinity/utils';
import Badge from '$lib/components/ui/Badge.svelte';
import { i18n } from '$lib/stores/app/i18n.store';
import type { Workflow, WorkflowKey } from '$lib/types/workflow';
import { toRepositoryKey } from '$lib/utils/workflow.utils';

interface Props {
key: WorkflowKey;
workflow: Workflow;
}

let { key, workflow }: Props = $props();

let repoKey = $derived(toRepositoryKey(key));

let { ref } = $derived(workflow.data);

let refType = $derived<'branch' | 'tag' | 'pull' | undefined>(
ref?.startsWith('refs/heads/')
? 'branch'
: ref?.startsWith('refs/tags/')
? 'tag'
: ref?.startsWith('refs/pull/')
? 'pull'
: undefined
);

let refName = $derived(
ref
?.replace('refs/heads/', '')
.replace('refs/tags/', '')
.replace(/refs\/pull\/(\d+)\/.*/, '$1') ?? ''
);

let pathname = $derived(`${repoKey.owner}/${repoKey.name}`);

let refHref = $derived(
refType === 'branch'
? `https://github.com/${pathname}/tree/${refName}`
: refType === 'tag'
? `https://github.com/${pathname}/releases/tag/${refName}`
: refType === 'pull'
? `https://github.com/${pathname}/pull/${refName}`
: undefined
);
</script>

{#if notEmptyString(refHref)}
<a
aria-label={$i18n.automation.view_branch}
href={refHref}
rel="noopener noreferrer"
target="_blank"><Badge color="primary-opaque">{refName}</Badge></a
>
{/if}

<style lang="scss">
a {
text-decoration: none;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<script lang="ts">
import Workflow from '$lib/components/automation/workflows/Workflow.svelte';
import WorkflowRef from '$lib/components/automation/workflows/WorkflowRef.svelte';
import type { Workflow as WorkflowType, WorkflowKey } from '$lib/types/workflow';
import { formatToDate } from '$lib/utils/date.utils';

interface Props {
key: string;
workflow: WorkflowType;
}

let { key, workflow }: Props = $props();

let { created_at } = $derived(workflow);
let workflowKey = $derived(key as WorkflowKey);
</script>

<tr>
<td class="workflow"><Workflow key={workflowKey} {workflow} /></td>
<td class="reference"><WorkflowRef key={workflowKey} {workflow} /></td>
<td class="timestamp">{formatToDate(created_at)}</td>
</tr>

<style lang="scss">
@use '../../../styles/mixins/media';

.workflow,
.reference {
width: 35%;
}

.timestamp {
width: 30%;
text-align: right;
}

.timestamp {
display: none;

@include media.min-width(small) {
display: table-cell;
}
}
</style>
102 changes: 102 additions & 0 deletions src/frontend/src/lib/components/automation/workflows/Workflows.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<script lang="ts">
import { nonNullish } from '@dfinity/utils';
import type { Principal } from '@icp-sdk/core/principal';

Check warning on line 3 in src/frontend/src/lib/components/automation/workflows/Workflows.svelte

View workflow job for this annotation

GitHub Actions / lint

'Principal' is defined but never used. Allowed unused vars must match /^_/u
import { getContext } from 'svelte';
import WorkflowFilter from '$lib/components/automation/workflows/WorkflowFilter.svelte';
import WorkflowRow from '$lib/components/automation/workflows/WorkflowRow.svelte';
import DataActions from '$lib/components/data/DataActions.svelte';
import DataCount from '$lib/components/data/DataCount.svelte';
import DataOrder from '$lib/components/data/DataOrder.svelte';
import DataPaginator from '$lib/components/data/DataPaginator.svelte';
import IconRefresh from '$lib/components/icons/IconRefresh.svelte';
import { i18n } from '$lib/stores/app/i18n.store';
import { PAGINATION_CONTEXT_KEY, type PaginationContext } from '$lib/types/pagination.context';
import type { Workflow } from '$lib/types/workflow';

interface Props {
reload: () => void;
}

let { reload }: Props = $props();

const { store: paginationStore }: PaginationContext<Workflow> =
getContext<PaginationContext<Workflow>>(PAGINATION_CONTEXT_KEY);

let empty = $derived($paginationStore.items?.length === 0);

let innerWidth = $state(0);

let colspan = $derived(innerWidth >= 576 ? 3 : 2);
</script>

<svelte:window bind:innerWidth />

<div class="table-container">
<table>
<thead>
<tr>
<th {colspan}>
<div class="actions">
<WorkflowFilter />
<DataOrder />

<DataActions>
<button class="menu" onclick={reload} type="button"
><IconRefresh size="20px" /> {$i18n.core.reload}</button
>
</DataActions>
</div>
</th>
</tr>
<tr>
<th class="workflow"> {$i18n.automation.workflow} </th>
<th class="reference"> {$i18n.automation.reference} </th>
<th class="timestamp"> {$i18n.automation.timestamp} </th>
</tr>
</thead>

<tbody>
{#if nonNullish($paginationStore.items)}
{#each $paginationStore.items as [key, workflow] (key)}
<WorkflowRow {key} {workflow} />
{/each}

{#if !empty && ($paginationStore.pages ?? 0) > 1}
<tr><td {colspan}><DataPaginator /></td></tr>
{/if}

{#if empty}
<tr><td {colspan}>{$i18n.automation.empty}</td></tr>
{/if}
{/if}
</tbody>
</table>
</div>

<DataCount />

<style lang="scss">
@use '../../../styles/mixins/media';

table {
table-layout: auto;
}

.workflow,
.reference {
width: 35%;
}

.timestamp {
width: 30%;
text-align: right;
}

.timestamp {
display: none;

@include media.min-width(small) {
display: table-cell;
}
}
</style>
Loading