@seeka-labs/cli-apps 3.5.2 → 3.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/index.js +47183 -0
  2. package/dist/index.js.map +7 -0
  3. package/dist/init-template/.gitlab-ci.yml +67 -0
  4. package/dist/init-template/.nvmrc +1 -0
  5. package/dist/init-template/README.md +27 -0
  6. package/dist/init-template/app/.eslintrc.cjs +13 -0
  7. package/dist/init-template/app/browser/README.md +1 -0
  8. package/dist/init-template/app/browser/package.json +37 -0
  9. package/dist/init-template/app/browser/scripts/esbuild/build-browser-plugin.mjs +130 -0
  10. package/dist/init-template/app/browser/scripts/esbuild/plugins/importAsGlobals.mjs +39 -0
  11. package/dist/init-template/app/browser/src/browser.ts +12 -0
  12. package/dist/init-template/app/browser/src/plugin/index.ts +61 -0
  13. package/dist/init-template/app/browser/tsconfig.json +34 -0
  14. package/dist/init-template/app/lib/package.json +46 -0
  15. package/dist/init-template/app/lib/src/index.ts +4 -0
  16. package/dist/init-template/app/lib/src/models/index.ts +29 -0
  17. package/dist/init-template/app/lib/src/validation/index.ts +14 -0
  18. package/dist/init-template/app/lib/tsconfig.json +22 -0
  19. package/dist/init-template/app/server-azurefunc/.eslintrc.cjs +4 -0
  20. package/dist/init-template/app/server-azurefunc/.funcignore +19 -0
  21. package/dist/init-template/app/server-azurefunc/README.md +105 -0
  22. package/dist/init-template/app/server-azurefunc/host.json +31 -0
  23. package/dist/init-template/app/server-azurefunc/local.settings.template.json +34 -0
  24. package/dist/init-template/app/server-azurefunc/package.json +67 -0
  25. package/dist/init-template/app/server-azurefunc/scripts/dev-queue-setup.js +55 -0
  26. package/dist/init-template/app/server-azurefunc/src/app/api/router.ts +14 -0
  27. package/dist/init-template/app/server-azurefunc/src/app/api/routes/getInstallationSettings.ts +13 -0
  28. package/dist/init-template/app/server-azurefunc/src/app/api/routes/setInstallationSettings.ts +35 -0
  29. package/dist/init-template/app/server-azurefunc/src/app/jobs/index.ts +61 -0
  30. package/dist/init-template/app/server-azurefunc/src/app/logging/index.ts +4 -0
  31. package/dist/init-template/app/server-azurefunc/src/app/models/index.ts +12 -0
  32. package/dist/init-template/app/server-azurefunc/src/app/services/activites.ts +8 -0
  33. package/dist/init-template/app/server-azurefunc/src/functions/healthCheck.ts +19 -0
  34. package/dist/init-template/app/server-azurefunc/src/functions/seekaAppWebhook.ts +202 -0
  35. package/dist/init-template/app/server-azurefunc/src/functions/trackActivityQueueHandler.ts +49 -0
  36. package/dist/init-template/app/server-azurefunc/src/functions/ui.ts +51 -0
  37. package/dist/init-template/app/server-azurefunc/tsconfig.json +24 -0
  38. package/dist/init-template/app/ui/README.md +40 -0
  39. package/dist/init-template/app/ui/index.html +21 -0
  40. package/dist/init-template/app/ui/package.json +72 -0
  41. package/dist/init-template/app/ui/public/favicon.ico +0 -0
  42. package/dist/init-template/app/ui/scripts/copy-output.mjs +30 -0
  43. package/dist/init-template/app/ui/src/App.tsx +72 -0
  44. package/dist/init-template/app/ui/src/assets/app-icon.svg +1 -0
  45. package/dist/init-template/app/ui/src/components/setup/steps/complete/index.tsx +32 -0
  46. package/dist/init-template/app/ui/src/components/setup/steps/first/index.tsx +27 -0
  47. package/dist/init-template/app/ui/src/components/setup/steps/index.tsx +22 -0
  48. package/dist/init-template/app/ui/src/components/setup/steps/second/index.tsx +38 -0
  49. package/dist/init-template/app/ui/src/index.tsx +45 -0
  50. package/dist/init-template/app/ui/src/routes/home/index.tsx +21 -0
  51. package/dist/init-template/app/ui/src/vite-env.d.ts +13 -0
  52. package/dist/init-template/app/ui/tsconfig.json +35 -0
  53. package/dist/init-template/app/ui/tsconfig.node.json +10 -0
  54. package/dist/init-template/app/ui/vite.config.mts +48 -0
  55. package/dist/init-template/package.json +44 -0
  56. package/dist/init-template/tsconfig.json +24 -0
  57. package/package.json +1 -1
  58. package/README.md +0 -1
@@ -0,0 +1,202 @@
1
+ import * as winston from 'winston';
2
+ import '../app/logging'
3
+
4
+ import {app, HttpRequest, HttpResponseInit, InvocationContext} from '@azure/functions';
5
+ import {
6
+ SeekaActivityAcceptedWebhookPayload, SeekaAppInstalledWebhookPayload,
7
+ SeekaAppUninstalledWebhookPayload,
8
+ SeekaIdentityChangedWebhookPayload, SeekaWebhookCallType, SeekaWebhookPayload,
9
+ throwOnInvalidWebhookSignature
10
+ } from '@seeka-labs/sdk-apps-server';
11
+
12
+ import {tryGetInstallation,
13
+ createOrUpdateInstallation, deleteInstallation, getSeekaBrowserPlugin, startServices,
14
+ webhookLogger,
15
+ childLogger,
16
+ } from '@seeka-labs/sdk-apps-server-host';
17
+
18
+ import { validateInstallationSettings, ExampleAppAppBrowserSdkPluginConfig, ExampleAppAppInstallContext} from '@example-org-name/example-app-name-lib'
19
+
20
+ import type {Logger} from 'winston';
21
+ import {queueNames, triggerBackgroundJob} from '../app/jobs';
22
+ import {TrackExampleAppActivityQueueItem} from "../app/models";
23
+
24
+ app.http('seekaAppWebhook', {
25
+ methods: ['POST'],
26
+ authLevel: 'anonymous',
27
+ route: 'api/webhook/seeka/app',
28
+ handler: seekaAppWebhook
29
+ });
30
+
31
+ export async function seekaAppWebhook(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
32
+ const bodyStr = (await req.text()) as string;
33
+ if (!bodyStr) {
34
+ return {
35
+ status: 400,
36
+ jsonBody: {error: "Body missing. Ensure body is present and either specify the Content-Length request header OR set Transfer-Encoding request header to 'chunked'"}
37
+ }
38
+ }
39
+ const body = JSON.parse(bodyStr) as SeekaWebhookPayload;
40
+
41
+ let logger = webhookLogger(body, context);
42
+ logger.profile('http.seeka.webhook.app')
43
+ logger.verbose('Received webhook from Seeka', {body});
44
+
45
+ // Handle probe
46
+ if (body.type === SeekaWebhookCallType.Probe) {
47
+ return {
48
+ status: 204
49
+ }
50
+ }
51
+
52
+ // Validate webhook
53
+ try {
54
+ throwOnInvalidWebhookSignature(process.env.SEEKA_APP_SECRET as string, req.headers as Headers, bodyStr);
55
+ logger.debug('Webhook signature validated', {body});
56
+ } catch {
57
+ logger.warn('Webhook signature invalid', {body});
58
+ return {
59
+ status: 401,
60
+ jsonBody: {error: "Webhook call invalid"}
61
+ }
62
+ }
63
+
64
+ if (body.isTest) {
65
+ // This is a test webhook call
66
+ return {
67
+ status: 204
68
+ }
69
+ }
70
+
71
+ await startServices(logger);
72
+
73
+ // Check if the webhook is for an app we have installed
74
+ let installation: ExampleAppAppInstallContext | null = null;
75
+ if (body.type != SeekaWebhookCallType.AppInstalled) {
76
+ installation = await tryGetInstallation((body as SeekaAppInstalledWebhookPayload).context?.applicationInstallId as string, false, logger);
77
+ if (installation == null) {
78
+ logger.warn('Webhook call cannot be processed as the installation ID is not known by this app', {body});
79
+
80
+ return {
81
+ status: 422,
82
+ jsonBody: {error: "App not installed"}
83
+ }
84
+ }
85
+ }
86
+
87
+ logger = childLogger(null, installation, logger, context);
88
+
89
+ // Do something
90
+ let errorMessage: string | null = null;
91
+ try {
92
+ switch (body.type) {
93
+ case SeekaWebhookCallType.AppInstalled: {
94
+ errorMessage = await onInstallation(body as SeekaAppInstalledWebhookPayload, logger);
95
+ break;
96
+ }
97
+ case SeekaWebhookCallType.AppUninstalled: {
98
+ if (!body.isTest) {
99
+ const payload = body as SeekaAppUninstalledWebhookPayload;
100
+ await deleteInstallation(payload.context?.applicationInstallId as string, logger)
101
+ }
102
+ break;
103
+ }
104
+ case SeekaWebhookCallType.ActivityAccepted: {
105
+ const payload = body as SeekaActivityAcceptedWebhookPayload;
106
+ await handleSeekaActivity(payload, installation, context, logger);
107
+
108
+ break;
109
+ }
110
+ case SeekaWebhookCallType.IdentityChanged: {
111
+ const payload = body as SeekaIdentityChangedWebhookPayload;
112
+
113
+ logger.debug('Identity changed webhook received', {personId: payload.content.personId });
114
+
115
+ break;
116
+ }
117
+
118
+ case SeekaWebhookCallType.BrowserSdkPlugin: {
119
+ // Configuration object passed to the Seeka browser plugin
120
+ // Under no circumstances should internal or private api keys be exposed to the client/browser via the below object
121
+ const pluginConfig = {
122
+ appId: process.env.SEEKA_APP_ID,
123
+ appInstallId: installation.applicationInstallId,
124
+ appUrl: process.env.SELF_HOST_BASEURL,
125
+ installationSettings: {
126
+ browserPluginAppSetting1: installation.installationState?.installationSettings?.browserPluginAppSetting1
127
+ }
128
+ } as ExampleAppAppBrowserSdkPluginConfig;
129
+ const plugin = await getSeekaBrowserPlugin<ExampleAppAppBrowserSdkPluginConfig>('ExampleAppAppConvergeSdkPlugin', pluginConfig, logger);
130
+
131
+ logger.profile('http.seeka.webhook.app')
132
+ return {
133
+ status: 200,
134
+ body: JSON.stringify(plugin),
135
+ headers: {
136
+ 'Content-Type': 'application/json'
137
+ }
138
+ }
139
+ }
140
+ }
141
+ } catch (err) {
142
+ logger.error('Failed to handle webhook', {ex: winston.exceptions.getAllInfo(err)});
143
+ return {
144
+ status: 500,
145
+ jsonBody: {error: "Request failed"}
146
+ }
147
+ } finally {
148
+ logger.profile('http.seeka.webhook.app')
149
+ logger.verbose('Seeka webhook handled');
150
+ }
151
+
152
+ if (errorMessage) {
153
+ logger.warn('Webhook call failed', {errorMessage});
154
+ return {
155
+ status: 400,
156
+ jsonBody: {error: {message: errorMessage}}
157
+ }
158
+ }
159
+
160
+ return {
161
+ status: 204
162
+ }
163
+ }
164
+
165
+ const onInstallation = async (payload: SeekaAppInstalledWebhookPayload, logger: Logger): Promise<string | null> => {
166
+ if (payload.isTest) return null;
167
+
168
+ const errorMessage = await validateInstallationSettings(payload.content?.installationSettings || {}, logger);
169
+ if (errorMessage) return errorMessage;
170
+
171
+ const installation = await createOrUpdateInstallation({
172
+ ...payload.context,
173
+ installationState: {
174
+ grantedPermissions: payload.content?.grantedPermissions || [],
175
+ installationSettings: payload.content?.installationSettings || {}
176
+ },
177
+ applicationInstallId: payload.context?.applicationInstallId as string,
178
+ organisationBrandId: payload.context?.organisationBrandId as string,
179
+ organisationId: payload.context?.organisationId as string,
180
+ installedAt: new Date().toISOString(),
181
+ }, logger)
182
+
183
+ return null;
184
+ }
185
+
186
+ const handleSeekaActivity = async (activity: SeekaActivityAcceptedWebhookPayload, installation: ExampleAppAppInstallContext, context: InvocationContext, logger: Logger) => {
187
+ logger = childLogger({
188
+ 'Activity_ActivityIdentifier': activity?.content?.activity?.activityId,
189
+ 'Tracking_Source_Url': activity?.content?.source?.loc,
190
+ 'Activity_Name': activity?.content?.activity?.activityNameCustom || activity?.content?.activity?.activityName?.toString()
191
+ }, installation, logger, context);
192
+
193
+ await triggerBackgroundJob(queueNames.trackActivity, {
194
+ payload: activity.content,
195
+ organisationBrandId: installation.organisationBrandId,
196
+ organisationId: installation.organisationId,
197
+ applicationInstallId: installation.applicationInstallId,
198
+ causationId: context.invocationId,
199
+ correlationId: context.invocationId
200
+ } as TrackExampleAppActivityQueueItem, logger)
201
+ }
202
+
@@ -0,0 +1,49 @@
1
+ import * as winston from 'winston';
2
+ import '../app/logging'
3
+
4
+ import {app, InvocationContext} from '@azure/functions';
5
+
6
+ import {deserialiseQueuePayload, queueNames, sendQueueMessageToPoisonQueue} from '../app/jobs';
7
+ import {sendActivityToAnotherSoftware} from "../app/services/activites";
8
+ import {TrackExampleAppActivityQueueItem} from "../app/models";
9
+ import {backgroundJobLogger, childLogger, startServices, tryGetInstallation} from "@seeka-labs/sdk-apps-server-host";
10
+
11
+ app.storageQueue('trackActivityQueueHandler', {
12
+ queueName: queueNames.trackActivity,
13
+ connection: 'AzureWebJobsStorage',
14
+ handler: trackActivityQueueHandler
15
+ });
16
+
17
+ export async function trackActivityQueueHandler(queueItem: unknown, context: InvocationContext): Promise<void> {
18
+ const body = typeof queueItem === 'string' ? deserialiseQueuePayload<TrackExampleAppActivityQueueItem>(queueItem) : queueItem as TrackExampleAppActivityQueueItem;
19
+ let logger = backgroundJobLogger(queueNames.trackActivity, body, context);
20
+ logger.profile(`queue.${queueNames.trackActivity}`)
21
+
22
+ await startServices(logger);
23
+
24
+ const applicationInstallId = body.applicationInstallId as string || (body.payload as any)?.applicationInstallId as string; // Support legacy payload / previous bug
25
+
26
+ const installation = await tryGetInstallation(applicationInstallId as string, true, logger);
27
+
28
+ logger = childLogger({
29
+ 'Activity_ActivityIdentifier': body?.payload?.seekaActivity?.activity?.activityId,
30
+ 'Tracking_Source_Url': body?.payload?.seekaActivity?.source?.loc,
31
+ 'Activity_Name': body?.payload?.seekaActivity?.activity?.activityNameCustom || body?.payload?.seekaActivity?.activity?.activityName?.toString()
32
+ }, installation, logger, context)
33
+
34
+ logger.info('Received request to track activity for ExampleApp', {body});
35
+
36
+ try {
37
+ await sendActivityToAnotherSoftware(body.payload, installation, context.invocationId, context.invocationId, logger);
38
+ } catch (err) {
39
+ const ex: any = winston.exceptions.getAllInfo(err);
40
+ logger.error('Error handling track ExampleApp activity queue item', {ex});
41
+
42
+ await sendQueueMessageToPoisonQueue(queueNames.trackActivity, {
43
+ ...body,
44
+ correlationId: context.invocationId,
45
+ } as TrackExampleAppActivityQueueItem, logger);
46
+ }
47
+
48
+ logger.profile(`queue.${queueNames.trackActivity}`)
49
+ }
@@ -0,0 +1,51 @@
1
+ import {app, HttpRequest, HttpResponseInit, InvocationContext} from '@azure/functions';
2
+ import '../app/logging'
3
+ import {
4
+ ExampleAppAppInstallContext,
5
+ ExampleAppAppInstallState,
6
+ ExampleAppAppUiClientInitState
7
+ } from "@example-org-name/example-app-name-lib";
8
+ import { SeekaAppInstallContext } from "@seeka-labs/sdk-apps-core";
9
+
10
+ import { generateAppUiHttpRequestResponse, HttpMethod} from '@seeka-labs/sdk-apps-server-host';
11
+ import {Logger} from "winston";
12
+ import {apiRouteHandler} from "../app/api/router";
13
+
14
+ // Define your route and handler for the /ui path
15
+ app.http('ui', {
16
+ methods: ['GET', 'HEAD', 'POST', 'DELETE'],
17
+ authLevel: 'anonymous',
18
+ route: 'app/{*route}', // Wildcard route under /ui
19
+ handler: ui
20
+ });
21
+
22
+ // Function to serve the React app under /app
23
+ export async function ui(req: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
24
+ const createBrowserInitState = async (installation: ExampleAppAppInstallContext, logger: Logger ): Promise<ExampleAppAppUiClientInitState> => {
25
+ // Under no circumstances should internal or private api keys be exposed to the client/browser via the below object
26
+ // Below state will be passed in plain text to the UI app, important to be selective (no spread operators on installation state)
27
+ // and not expose any internal or private api keys to the client/browser
28
+ return {
29
+ exampleInstallSetting1: installation?.installationState?.installationSettings?.exampleInstallSetting1
30
+ }
31
+ }
32
+
33
+ const res = await generateAppUiHttpRequestResponse(
34
+ req.url,
35
+ req.method as HttpMethod,
36
+ req.headers as Headers,
37
+ context,
38
+ createBrowserInitState,
39
+ async(...args) => {
40
+ return await apiRouteHandler(req, context, ...args)
41
+ }
42
+ )
43
+
44
+ return {
45
+ status: res.status,
46
+ headers: res.headers as HeadersInit,
47
+ cookies: res.cookies,
48
+ body: res.body as HttpResponseInit['body'],
49
+ jsonBody: res.jsonBody
50
+ };
51
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "skipLibCheck": true,
5
+ "strict": false,
6
+ "esModuleInterop": true,
7
+ "outDir": "dist",
8
+ "rootDir": ".",
9
+ "sourceMap": true,
10
+ "module": "CommonJS",
11
+ "target": "ES6",
12
+ "resolveJsonModule": true,
13
+ "allowSyntheticDefaultImports": true,
14
+ "moduleResolution": "node",
15
+ "isolatedModules": false
16
+ },
17
+ "include": [
18
+ "src/**/*.ts",
19
+ "src/definitions.d.ts"
20
+ ],
21
+ "exclude": [
22
+ "node_modules"
23
+ ]
24
+ }
@@ -0,0 +1,40 @@
1
+ # Material UI - Create React App example in TypeScript
2
+
3
+ ## How to use
4
+
5
+ Download the example [or clone the repo](https://github.com/mui/material-ui):
6
+
7
+ <!-- #default-branch-switch -->
8
+
9
+ ```bash
10
+ curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/material-ui-cra-ts
11
+ cd material-ui-cra-ts
12
+ ```
13
+
14
+ Install it and run:
15
+
16
+ ```bash
17
+ npm install
18
+ npm start
19
+ ```
20
+
21
+ or:
22
+
23
+ <!-- #default-branch-switch -->
24
+
25
+ [![Edit on CodeSandbox](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/p/sandbox/github/mui/material-ui/tree/master/examples/material-ui-cra-ts)
26
+
27
+ [![Edit on StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/mui/material-ui/tree/master/examples/material-ui-cra-ts)
28
+
29
+ ## The idea behind the example
30
+
31
+ This example demonstrates how you can use Material UI with [Create React App](https://github.com/facebookincubator/create-react-app) in [TypeScript](https://github.com/Microsoft/TypeScript).
32
+ It includes `@mui/material` and its peer dependencies, including [Emotion](https://emotion.sh/docs/introduction), the default style engine in Material UI v6.
33
+ If you prefer, you can [use styled-components instead](https://mui.com/material-ui/integrations/interoperability/#styled-components).
34
+
35
+ ## What's next?
36
+
37
+ <!-- #default-branch-switch -->
38
+
39
+ You now have a working example project.
40
+ You can head back to the documentation and continue by browsing the [templates](https://mui.com/material-ui/getting-started/templates/) section.
@@ -0,0 +1,21 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="UTF-8"/>
6
+ <link rel="icon" href="/favicon.ico"/>
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
8
+ <title>Seeka ExampleApp</title>
9
+ <!-- Fonts to support Material Design -->
10
+ <link rel="preconnect" href="https://fonts.googleapis.com"/>
11
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
12
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet"/>
13
+ <script vite-ignore src="%VITE_BASE_URL%config.js?c=%VITE_RELEASE_TAG%"></script>
14
+ <script type="module" src="/src/index.tsx"></script>
15
+ </head>
16
+ <body>
17
+ <noscript>You need to enable JavaScript to run this app.</noscript>
18
+ <div id="root"></div>
19
+ </body>
20
+
21
+ </html>
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@example-org-name/example-app-name-ui",
3
+ "version": "3.5.4",
4
+ "description": "Seeka app UI for example-app-name",
5
+ "author": "Seeka <administrator@seeka.co>",
6
+ "license": "MIT",
7
+ "homepage": "/app",
8
+ "private": true,
9
+ "dependencies": {
10
+ "@emotion/react": "^11",
11
+ "@emotion/styled": "^11",
12
+ "@example-org-name/example-app-name-lib": "workspace:*",
13
+ "@hookform/resolvers": "^5",
14
+ "@mui/icons-material": "7",
15
+ "@mui/lab": "7.0.1-beta.20",
16
+ "@mui/material": "^7",
17
+ "@mui/styles": "^6",
18
+ "@mui/system": "^7",
19
+ "@reduxjs/toolkit": "^2",
20
+ "@seeka-labs/converge": "^1",
21
+ "@seeka-labs/sdk-apps-core": "../../../workspace:* || ^3",
22
+ "@seeka-labs/sdk-apps-react": "../../../workspace:* || ^3",
23
+ "lodash-es": "^4",
24
+ "material-ui-confirm": "^4",
25
+ "notistack": "^3",
26
+ "query-string": "^9",
27
+ "react": "^18",
28
+ "react-dom": "^18",
29
+ "react-hook-form": "^7",
30
+ "react-redux": "^9",
31
+ "react-router-dom": "^7",
32
+ "redux": "^5",
33
+ "redux-persist": "^6",
34
+ "uuid": "^13",
35
+ "vite-plugin-svgr": "^4",
36
+ "yup": "^1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/fs-extra": "^11",
40
+ "@types/lodash-es": "^4",
41
+ "@types/react": "^18",
42
+ "@types/react-dom": "^18",
43
+ "@vitejs/plugin-react": "^5",
44
+ "cross-env": "^10",
45
+ "fs-extra": "^11",
46
+ "rimraf": "^6",
47
+ "typescript": "^5",
48
+ "vite": "^7"
49
+ },
50
+ "scripts": {
51
+ "start": "vite",
52
+ "typecheck": "tsc --noEmit",
53
+ "clean": "rimraf build package dist",
54
+ "copy-output": "node ./scripts/copy-output.mjs",
55
+ "dev": "cross-env NODE_ENV=development cross-env VITE_RELEASE_TAG=$(date +%Y%m%d%H%M%S) cross-env VITE_BASE_URL=/app/ vite",
56
+ "build": "vite build && yarn copy-output",
57
+ "build:dev": "cross-env NODE_ENV=development VITE_RELEASE_TAG=$(date +%Y%m%d%H%M%S) VITE_BASE_URL=/app/ vite build --watch",
58
+ "preview": "vite preview"
59
+ },
60
+ "browserslist": {
61
+ "production": [
62
+ ">0.2%",
63
+ "not dead",
64
+ "not op_mini all"
65
+ ],
66
+ "development": [
67
+ "last 1 chrome version",
68
+ "last 1 firefox version",
69
+ "last 1 safari version"
70
+ ]
71
+ }
72
+ }
@@ -0,0 +1,30 @@
1
+
2
+ import { existsSync, mkdirSync, cpSync } from 'fs';
3
+ import { resolve } from 'path';
4
+ import {rmSync} from "node:fs";
5
+
6
+ const postBuild = () => {
7
+ const serverDirNames = ['server-azurefunc']
8
+
9
+ serverDirNames.forEach(dirName => {
10
+ if (existsSync(resolve(`../${dirName}`))) {
11
+
12
+ if (!existsSync(resolve(`../${dirName}/dist/src/ui`))) {
13
+ mkdirSync(resolve(`../${dirName}/dist/src/ui`), {
14
+ recursive: true,
15
+ });
16
+ }
17
+ rmSync(resolve(`../${dirName}/dist/src/ui`), { recursive: true });
18
+ mkdirSync(resolve(`../${dirName}/dist/src/ui`), {
19
+ recursive: true,
20
+ });
21
+
22
+ // Copy all files in current working dir ../build dir and all files to `../${dirName}/dist/src/ui
23
+ cpSync(resolve(`./build`), resolve(`../${dirName}/dist/src/ui/build`), {
24
+ recursive: true,
25
+ });
26
+ }
27
+ })
28
+ }
29
+
30
+ postBuild();
@@ -0,0 +1,72 @@
1
+ import { Container, Paper } from '@mui/material';
2
+ import Box from '@mui/material/Box';
3
+ import { ConfirmProvider } from 'material-ui-confirm';
4
+ import * as React from 'react';
5
+ import {
6
+ createBrowserRouter,
7
+ Outlet,
8
+ RouterProvider,
9
+ } from "react-router-dom";
10
+ import { Index } from './routes/home';
11
+ import {TokenProvider, SeekaCoreApiReduxProvider} from "@seeka-labs/sdk-apps-react";
12
+ import {ExampleAppAppUiClientInitConfig} from "@example-org-name/example-app-name-lib";
13
+
14
+ let basename = (import.meta as any).env.BASE_URL
15
+ // trim last /
16
+ basename = basename.substring(0, basename.length - 1);
17
+
18
+ function AppLayout() {
19
+ return (
20
+ <TokenProvider>
21
+ <SeekaCoreApiReduxProvider>
22
+ <ConfirmProvider>
23
+ <Outlet />
24
+ </ConfirmProvider>
25
+ </SeekaCoreApiReduxProvider>
26
+ </TokenProvider>
27
+ );
28
+ }
29
+
30
+ const router = createBrowserRouter([
31
+ {
32
+ element: <AppLayout />,
33
+ children: [
34
+ { path: '/', element: <Index /> },
35
+ // other routes…
36
+ ],
37
+ },
38
+ ], { basename });
39
+
40
+ declare global {
41
+ interface Window {
42
+ seekaAppConfig: ExampleAppAppUiClientInitConfig
43
+ }
44
+ }
45
+
46
+ export default function App() {
47
+ const [loaded, setLoaded] = React.useState(Boolean(window.seekaAppConfig));
48
+
49
+ let interval: any = null;
50
+ if (!loaded && !interval) {
51
+ interval = setInterval(() => {
52
+ setLoaded(Boolean(window.seekaAppConfig || process.env.NODE_ENV === 'development'));
53
+ }, 200);
54
+ }
55
+
56
+ React.useEffect(() => {
57
+ if (loaded && interval) {
58
+ clearInterval(interval);
59
+ }
60
+ }, [loaded]);
61
+
62
+ return (
63
+ !loaded ? <></> : (
64
+ <Box sx={{ my: 4, mx: 2 }}>
65
+ <Container maxWidth='lg'>
66
+ <Paper sx={{ bgcolor: 'white', p: 3 }} elevation={0}>
67
+ <RouterProvider router={router} />
68
+ </Paper></Container>
69
+ </Box>
70
+ )
71
+ );
72
+ }
@@ -0,0 +1 @@
1
+ <?xml version='1.0' encoding='utf-8'?><!-- Generator: Adobe Illustrator 25.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) --><svg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 268.4 84.7' style='enable-background:new 0 0 268.4 84.7;' xml:space='preserve'><style type='text/css'> .st0{fill:#722ED1;} .st1{enable-background:new ;} </style><path d='M0,72.5c0,6.7,5.5,12.2,12.2,12.2l0,0l0,0l0,0c3.2,0,6.3-1.3,8.6-3.6l12.5-12.6c-0.4,0-0.7,0-1.1,0l0,0 c-4.2,0-8.4-1.7-11.4-4.7l-8.6-8.6l-8.6,8.6C1.3,66.1,0,69.3,0,72.5z'/><g><path d='M40.9,3.6C38.6,1.3,35.6,0,32.3,0S26,1.3,23.7,3.6L11.1,16.1c0.4,0,0.7,0,1.1,0c4.1,0,8.3,1.6,11.5,4.7l8.6,8.6l8.6-8.6 C45.7,16.1,45.7,8.3,40.9,3.6z'/><path d='M2.2,22.2L2.2,22.2L2.2,22.2z'/></g><path class='st0' d='M3.7,41L3.7,41c2.3,2.3,5.3,3.5,8.5,3.5c0,0,0,0,0.1,0c3.2,0,6.3-1.3,8.6-3.6l8.6-8.6l-8.6-8.6 c-2.4-2.4-5.5-3.6-8.6-3.6S6,21.3,3.6,23.7C-1.2,28.4-1.2,36.2,3.7,41L3.7,41z'/><g class='st1'><path d='M76.3,62.6C66,62.6,59.7,57.2,59.6,49h12.5c0,2.8,1.9,4,4.4,4c1.9,0,3.7-1,3.7-2.9c0-2.2-2.9-2.8-6.5-3.4 c-5.9-1-13.6-2.7-13.6-11.9c0-7.6,6.5-12.5,16.1-12.5s15.9,5.1,16,12.8H80.1c0-2.4-1.6-3.5-4-3.5c-2.1,0-3.5,1-3.5,2.8 c0,2.2,2.9,2.7,6.5,3.3C85,38.7,93,40,93,49.5C93,57.4,86.3,62.6,76.3,62.6L76.3,62.6z'/></g><g class='st1'><path d='M138,42.1c0,1.1-0.1,2.3-0.3,3.4h-28.1c0.9,4.3,3.6,6.6,7.7,6.6c3,0,5.6-1.3,6.8-3.5h13c-2.7,8.6-10.3,14-19.8,14 c-11.8,0-20.5-8.6-20.5-20.2s8.7-20.1,20.5-20.1C129.6,22.3,138,31,138,42.1L138,42.1z M109.9,38.3h15.3c-1.1-3.8-3.9-5.8-7.8-5.8 C113.6,32.5,111,34.6,109.9,38.3z'/></g><g class='st1'><path d='M183.6,42.1c0,1.1-0.1,2.3-0.3,3.4h-28.1c0.9,4.3,3.6,6.6,7.7,6.6c3,0,5.6-1.3,6.8-3.5h13c-2.7,8.6-10.3,14-19.8,14 c-11.8,0-20.5-8.6-20.5-20.2s8.7-20.1,20.5-20.1C175.1,22.3,183.6,31,183.6,42.1L183.6,42.1z M155.5,38.3h15.3 c-1.1-3.8-3.9-5.8-7.8-5.8C159.2,32.5,156.5,34.6,155.5,38.3z'/></g><g class='st1'><path d='M211.1,61.4l-9.5-16v16h-13V9.9h13v28.5l9-14.8h14.6l-11.8,18l13,19.8H211.1z'/><path d='M268.4,23.5v37.8h-10.1l-1.1-2.7c-3.1,2.5-7,4-11.4,4c-11.6,0-19.8-8.4-19.8-20.2c0-11.7,8.3-20,19.8-20 c4.5,0,8.4,1.5,11.6,4.1l1.3-2.9L268.4,23.5z M256,42.5c0-4.9-3.6-8.6-8.5-8.6s-8.5,3.7-8.5,8.6s3.6,8.6,8.5,8.6S256,47.4,256,42.5 z'/></g><path d='M12.2,48.5c-0.3,0-0.7,0-1,0L23.7,61c2.3,2.3,5.4,3.6,8.6,3.6l0,0l0,0l0,0c3.3,0,6.3-1.3,8.6-3.6c4.8-4.8,4.7-12.5,0-17.3 l-8.6-8.6l-8.6,8.6C20.7,46.8,16.5,48.5,12.2,48.5L12.2,48.5z'/></svg>
@@ -0,0 +1,32 @@
1
+ import { Button, Typography } from "@mui/material"
2
+ import { Stack } from "@mui/system"
3
+ import {SeekaAppWizardStep, SeekaAppWizardHeading, SeekaAppWizardStepContent, SeekaBotHappy, SeekaAppWizardStepActions, sendSeekaCoreAppCommand} from "@seeka-labs/sdk-apps-react";
4
+
5
+ type Props = {
6
+ onBack?: () => void
7
+ }
8
+
9
+ export const AppSetupCompletedStep = ({ onBack }: Props) => {
10
+ return (
11
+ <SeekaAppWizardStep>
12
+ <Stack direction={"row"} sx={{ width: '100%' }} gap={12} alignItems="center" justifyContent={"center"}>
13
+ <SeekaBotHappy style={{ width: '260px' }} />
14
+ <Stack gap={6} alignItems="flex-start" justifyContent={"center"}>
15
+ <Stack gap={3}>
16
+ <SeekaAppWizardHeading title="Success!" />_b
17
+
18
+ <Typography>ExampleApp™ is now connected to Seeka</Typography>
19
+ </Stack>
20
+
21
+ <SeekaAppWizardStepActions StackProps={{ gap: 3 }}>
22
+ <Button variant='text' color="primary" onClick={onBack}>Back</Button>
23
+ <Button variant='contained' color="primary" onClick={() => sendSeekaCoreAppCommand('dashboard')}>Back to dashboard</Button>
24
+ <Button variant='outlined' color="primary" onClick={() => sendSeekaCoreAppCommand('appsList')}>Connect another app</Button>
25
+ </SeekaAppWizardStepActions>
26
+ </Stack>
27
+
28
+ </Stack>
29
+
30
+ </SeekaAppWizardStep>
31
+ )
32
+ }
@@ -0,0 +1,27 @@
1
+ import { Button } from "@mui/material"
2
+ import seekaAppIcon from '../../../../assets/app-icon.svg'
3
+ import {SeekaAppWizardStep, SeekaAppWizardHeading, SeekaAppWizardStepContent, SeekaAppWizardStepActions} from "@seeka-labs/sdk-apps-react";
4
+
5
+ type Props = {
6
+ onNext?: () => void
7
+ }
8
+
9
+ export const AppSetupFirstStep = ({ onNext }: Props) => { return (
10
+ <SeekaAppWizardStep>
11
+ <SeekaAppWizardHeading title="Connect to ExampleApp™" image={seekaAppIcon}>
12
+ Connecting your ExampleApp™ to Seeka is super easy and can be done in around 3 minutes by anyone with Admin access to ExampleApp™.
13
+ </SeekaAppWizardHeading>
14
+ <SeekaAppWizardStepContent title="Instructions" subtitle="Authenticate ExampleApp">
15
+ <ol>
16
+ <li>Click the Authenticate ExampleApp button</li>
17
+ <li>Select the account that is authorised to your ExampleApp™ account</li>
18
+ <li>Follow the guided prompts to authorise Seeka.</li>
19
+ </ol>
20
+ </SeekaAppWizardStepContent>
21
+
22
+ <SeekaAppWizardStepActions>
23
+ {onNext && <Button variant='contained' color="primary" onClick={onNext}>Next</Button>}
24
+ </SeekaAppWizardStepActions>
25
+ </SeekaAppWizardStep>
26
+ )
27
+ }
@@ -0,0 +1,22 @@
1
+
2
+ import { Box } from "@mui/material";
3
+ import { useState } from "react";
4
+ import { AppSetupFirstStep } from "./first";
5
+ import { AppSetupSecondStep } from "./second";
6
+ import { AppSetupCompletedStep } from "./complete";
7
+ import {SeekaAppLoadingSpinner} from "@seeka-labs/sdk-apps-react";
8
+
9
+ export const SetupSteps = () => {
10
+ const [activeStep, setActiveStep] = useState<number | null>(0)
11
+ const [loading, setLoading] = useState(true)
12
+
13
+ if (activeStep === null || loading) return <Box><SeekaAppLoadingSpinner /></Box>
14
+
15
+ return (
16
+ <Box>
17
+ {activeStep === 0 && <AppSetupFirstStep onNext={() => setActiveStep(1)} />}
18
+ {activeStep === 0 && <AppSetupSecondStep onNext={() => setActiveStep(2)} onBack={() => setActiveStep(1)} />}
19
+ {activeStep === 3 && <AppSetupCompletedStep onBack={() => setActiveStep(1)} />}
20
+ </Box>
21
+ )
22
+ }