diff --git a/package-lock.json b/package-lock.json
index 378b7ff9a84..31f5cd4fc73 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23176,6 +23176,15 @@
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
"dev": true
},
+ "node_modules/cronstrue": {
+ "version": "2.41.0",
+ "resolved": "https://registry.npmjs.org/cronstrue/-/cronstrue-2.41.0.tgz",
+ "integrity": "sha512-3ZS3eMJaxMRBGmDauKCKbyIRgVcph6uSpkhSbbZvvJWkelHiSTzGJbBqmu8io7Hspd2F45bQKnC1kzoNvtku2g==",
+ "dev": true,
+ "bin": {
+ "cronstrue": "bin/cli.js"
+ }
+ },
"node_modules/cross-fetch": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz",
@@ -44716,6 +44725,7 @@
"autoprefixer": "^10.4.13",
"axios": "^0.27.2",
"cookie": "^0.4.1",
+ "cronstrue": "^2.41.0",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^4.0.0",
diff --git a/web-admin/package.json b/web-admin/package.json
index 4555e75c380..2357ce09cf4 100644
--- a/web-admin/package.json
+++ b/web-admin/package.json
@@ -16,9 +16,9 @@
"@fontsource/fira-mono": "^4.5.0",
"@playwright/test": "^1.25.0",
"@rgossiaux/svelte-headlessui": "^2.0.0",
+ "@rilldata/svelte-query": "^4.29.20-0.0.1",
"@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "^1.5.0",
- "@rilldata/svelte-query": "^4.29.20-0.0.1",
"@tanstack/svelte-query": "npm:@rilldata/svelte-query@4.29.20-0.0.1",
"@types/cookie": "^0.5.1",
"@typescript-eslint/eslint-plugin": "^5.27.0",
@@ -26,6 +26,7 @@
"autoprefixer": "^10.4.13",
"axios": "^0.27.2",
"cookie": "^0.4.1",
+ "cronstrue": "^2.41.0",
"eslint": "^8.16.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-svelte3": "^4.0.0",
diff --git a/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte b/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
index a2aeb00efe7..33f3e5fb07b 100644
--- a/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
+++ b/web-admin/src/features/dashboards/listing/DashboardsTableHeader.svelte
@@ -46,7 +46,7 @@
class="pl-2 pr-4 py-2 max-w-[800px] flex items-center gap-x-2 bg-slate-100"
>
-
+
diff --git a/web-admin/src/features/projects/ProjectTabs.svelte b/web-admin/src/features/projects/ProjectTabs.svelte
index 931297b00fc..526bf1d6e30 100644
--- a/web-admin/src/features/projects/ProjectTabs.svelte
+++ b/web-admin/src/features/projects/ProjectTabs.svelte
@@ -19,11 +19,10 @@
route: `/${organization}/${project}`,
label: "Dashboards",
},
- // Hide until releasing Scheduled Reports
- // {
- // route: `/${organization}/${project}/-/reports`,
- // label: "Reports",
- // },
+ {
+ route: `/${organization}/${project}/-/reports`,
+ label: "Reports",
+ },
];
const adminTabs = [
{
diff --git a/web-admin/src/features/scheduled-reports/listing/NoReportsCTA.svelte b/web-admin/src/features/scheduled-reports/listing/NoReportsCTA.svelte
new file mode 100644
index 00000000000..a013d36ccf7
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/NoReportsCTA.svelte
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ You don't have any reports yet
+
+
+
Learn how to create a report in our
+ docs
+
+
+
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsError.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsError.svelte
new file mode 100644
index 00000000000..6d794cc43c2
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsError.svelte
@@ -0,0 +1,15 @@
+
+
+
+
+
Error loading reports
+
+ If this error persists, please contact support.
+
+
+
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsTable.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsTable.svelte
new file mode 100644
index 00000000000..41ea991722a
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsTable.svelte
@@ -0,0 +1,90 @@
+
+
+{#if $reports.isLoading}
+
+
+
+{:else if $reports.isError}
+
+{:else if $reports.isSuccess}
+ {#if $reports.data.resources.length === 0}
+
+ {:else}
+
+ {/if}
+{/if}
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsTableActionCell.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsTableActionCell.svelte
new file mode 100644
index 00000000000..924bf3c7d95
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsTableActionCell.svelte
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsTableCompositeCell.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsTableCompositeCell.svelte
new file mode 100644
index 00000000000..2395f9970e8
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsTableCompositeCell.svelte
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+ {title}
+
+ {#if lastRun}
+ {#if lastRunErrorMessage}
+
+ {:else}
+
+ {/if}
+ {/if}
+
+
+ {#if !lastRun}
+ Hasn't run yet
+ {:else}
+ Last run {formatDateToCustomString(new Date(lastRun))}
+ {/if}
+ •
+ {humanReadableFrequency}
+ •
+ Created by {owner?.userName || "a project admin"}
+
+
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsTableEmpty.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsTableEmpty.svelte
new file mode 100644
index 00000000000..aee2025eb5d
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsTableEmpty.svelte
@@ -0,0 +1 @@
+
No reports found.
diff --git a/web-admin/src/features/scheduled-reports/listing/ReportsTableHeader.svelte b/web-admin/src/features/scheduled-reports/listing/ReportsTableHeader.svelte
new file mode 100644
index 00000000000..7aca7d5a172
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/listing/ReportsTableHeader.svelte
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {numReports} report{numReports !== 1 ? "s" : ""}
+
+
+
+ |
+
+
+
+
+
diff --git a/web-admin/src/features/scheduled-reports/selectors.ts b/web-admin/src/features/scheduled-reports/selectors.ts
new file mode 100644
index 00000000000..0068a982bb1
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/selectors.ts
@@ -0,0 +1,18 @@
+import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
+import {
+ createRuntimeServiceGetResource,
+ createRuntimeServiceListResources,
+} from "@rilldata/web-common/runtime-client";
+
+export function useReports(instanceId: string) {
+ return createRuntimeServiceListResources(instanceId, {
+ kind: ResourceKind.Report,
+ });
+}
+
+export function useReport(instanceId: string, name: string) {
+ return createRuntimeServiceGetResource(instanceId, {
+ "name.name": name,
+ "name.kind": ResourceKind.Report,
+ });
+}
diff --git a/web-admin/src/features/scheduled-reports/tableUtils.ts b/web-admin/src/features/scheduled-reports/tableUtils.ts
new file mode 100644
index 00000000000..120ce06f09c
--- /dev/null
+++ b/web-admin/src/features/scheduled-reports/tableUtils.ts
@@ -0,0 +1,20 @@
+export function formatDateToCustomString(date: Date) {
+ const formattedDate = date.toLocaleString("en-US", {
+ month: "short",
+ day: "2-digit",
+ year: "numeric",
+ hour: "numeric",
+ minute: "2-digit",
+ hour12: true,
+ });
+
+ const dateParts = formattedDate.split(", ");
+ dateParts[0] = dateParts[0] + " " + dateParts[1];
+ dateParts.splice(1, 1);
+
+ return dateParts.join(", ").replace(" PM", "pm").replace(" AM", "am");
+}
+
+export function capitalizeFirstLetter(string: string) {
+ return string.charAt(0).toUpperCase() + string.slice(1);
+}
diff --git a/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte b/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte
new file mode 100644
index 00000000000..0421bd8f15d
--- /dev/null
+++ b/web-admin/src/routes/[organization]/[project]/-/reports/+page.svelte
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/web-common/src/components/icons/CancelCircleInverse.svelte b/web-common/src/components/icons/CancelCircleInverse.svelte
index a99ae2f3075..935f5510b82 100644
--- a/web-common/src/components/icons/CancelCircleInverse.svelte
+++ b/web-common/src/components/icons/CancelCircleInverse.svelte
@@ -13,12 +13,12 @@
>
diff --git a/web-common/src/components/icons/CheckCircleOutline.svelte b/web-common/src/components/icons/CheckCircleOutline.svelte
new file mode 100644
index 00000000000..c492fe5d8b0
--- /dev/null
+++ b/web-common/src/components/icons/CheckCircleOutline.svelte
@@ -0,0 +1,24 @@
+
+
+
diff --git a/web-common/src/components/icons/ReportIcon.svelte b/web-common/src/components/icons/ReportIcon.svelte
new file mode 100644
index 00000000000..ba424c2e67f
--- /dev/null
+++ b/web-common/src/components/icons/ReportIcon.svelte
@@ -0,0 +1,20 @@
+
+
+
diff --git a/web-common/src/components/icons/ThreeDot.svelte b/web-common/src/components/icons/ThreeDot.svelte
new file mode 100644
index 00000000000..fb6e73db706
--- /dev/null
+++ b/web-common/src/components/icons/ThreeDot.svelte
@@ -0,0 +1,18 @@
+
+
+
diff --git a/web-common/src/features/entity-management/resource-selectors.ts b/web-common/src/features/entity-management/resource-selectors.ts
index 400a8cfa459..f140a90fd6d 100644
--- a/web-common/src/features/entity-management/resource-selectors.ts
+++ b/web-common/src/features/entity-management/resource-selectors.ts
@@ -15,6 +15,7 @@ export enum ResourceKind {
Source = "rill.runtime.v1.Source",
Model = "rill.runtime.v1.Model",
MetricsView = "rill.runtime.v1.MetricsView",
+ Report = "rill.runtime.v1.Report",
}
export const SingletonProjectParserName = "parser";