@microlight/core 0.2.0
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.
- package/README.md +77 -0
- package/bin/microlight-core.js +70 -0
- package/dist/scripts/generate-folder-index.js +120 -0
- package/dist/scripts/generate-task-imports.js +64 -0
- package/dist/scripts/generate-task-index.js +61 -0
- package/dist/scripts/prepareFolders.js +119 -0
- package/dist/scripts/prepareServer.js +34 -0
- package/dist/scripts/prepareTasks.js +114 -0
- package/dist/server/app/api/tasks/[slug]/route.js +54 -0
- package/dist/server/app/layout.js +41 -0
- package/dist/server/app/library/[[...f_path]]/ViewFolder.js +113 -0
- package/dist/server/app/library/[[...f_path]]/page.js +42 -0
- package/dist/server/app/page.js +4 -0
- package/dist/server/app/tasks/[slug]/ViewTask.js +252 -0
- package/dist/server/app/tasks/[slug]/action.js +44 -0
- package/dist/server/app/tasks/[slug]/page.js +33 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/ViewRun.js +230 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/DropdownActions.js +46 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/action.js +35 -0
- package/dist/server/app/tasks/[slug]/runs/[r_id]/page.js +43 -0
- package/dist/server/components/Icon.js +22 -0
- package/dist/server/components/Link.js +52 -0
- package/dist/server/components/MLInput.js +29 -0
- package/dist/server/components/Navbar/Navbar.js +38 -0
- package/dist/server/components/Navbar/NavbarContainer.js +26 -0
- package/dist/server/components/PageHeader.js +87 -0
- package/dist/server/components/StatusChip.js +11 -0
- package/dist/server/components/Test.js +5 -0
- package/dist/server/components/TopLoader.js +8 -0
- package/dist/server/database/microlight/index.js +52 -0
- package/dist/server/database/microlight/tables/Logs.model.js +34 -0
- package/dist/server/database/microlight/tables/Runs.model.js +61 -0
- package/dist/server/instrumentation.js +16 -0
- package/dist/server/lib/executeRun.js +80 -0
- package/dist/server/lib/generateDisplayFunctions.js +89 -0
- package/dist/server/lib/getAllTasks.js +32 -0
- package/dist/server/lib/getTaskDetails.js +17 -0
- package/dist/server/lib/loadSchedules.js +77 -0
- package/dist/server/tasks/1.intro/hello_world2.task.js +21 -0
- package/dist/server/tasks/1.intro/microlight.folder.js +5 -0
- package/dist/server/tasks/1.intro/ml.task.js +31 -0
- package/dist/server/tasks/1.intro/scheduled.task.js +18 -0
- package/dist/server/tasks/1.intro/takes_time.task.js +28 -0
- package/dist/server/tasks/1.intro/test/microlight.folder.js +5 -0
- package/dist/server/tasks/1.intro/test/takes_time2.task.js +28 -0
- package/dist/server/tasks/index.js +33 -0
- package/dist/server/tasks/microlight.folder.js +5 -0
- package/index.js +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import PageHeader from "../../../../../components/PageHeader";
|
|
4
|
+
import { Container, Table, Link, Chip, Typography, Sheet, Alert } from "@mui/joy";
|
|
5
|
+
import { useRouter } from 'next/navigation';
|
|
6
|
+
import { useState, useEffect } from 'react';
|
|
7
|
+
import StatusChip from "../../../../../components/StatusChip";
|
|
8
|
+
import DropdownActions from "./_components/DropdownActions/DropdownActions";
|
|
9
|
+
function generateBreadcrumbs({
|
|
10
|
+
task,
|
|
11
|
+
params
|
|
12
|
+
}) {
|
|
13
|
+
let breadcrumbs = [{
|
|
14
|
+
text: "Library",
|
|
15
|
+
href: "/library"
|
|
16
|
+
}];
|
|
17
|
+
// Add task path segments to breadcrumbs if available
|
|
18
|
+
if (task._folderPath) {
|
|
19
|
+
const f_path = task._folderPath.split('/');
|
|
20
|
+
let folderPath = '/library';
|
|
21
|
+
f_path.forEach((folder, index) => {
|
|
22
|
+
folderPath += '/' + folder;
|
|
23
|
+
breadcrumbs.push({
|
|
24
|
+
text: folder,
|
|
25
|
+
href: folderPath
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
breadcrumbs.push({
|
|
29
|
+
text: task.slug,
|
|
30
|
+
href: '/tasks/' + task.slug
|
|
31
|
+
});
|
|
32
|
+
breadcrumbs.push({
|
|
33
|
+
text: params.r_id
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return breadcrumbs;
|
|
37
|
+
}
|
|
38
|
+
export default function ViewRun({
|
|
39
|
+
params,
|
|
40
|
+
task,
|
|
41
|
+
run,
|
|
42
|
+
logs
|
|
43
|
+
}) {
|
|
44
|
+
const breadcrumbs = generateBreadcrumbs({
|
|
45
|
+
task,
|
|
46
|
+
params
|
|
47
|
+
});
|
|
48
|
+
const router = useRouter();
|
|
49
|
+
let RightButtons = function () {
|
|
50
|
+
return <>
|
|
51
|
+
<DropdownActions run={run} />
|
|
52
|
+
</>;
|
|
53
|
+
};
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
let intervalId;
|
|
56
|
+
if (run.status === 'running' || run.status === 'pending') {
|
|
57
|
+
intervalId = setInterval(() => {
|
|
58
|
+
// Refresh the page data using router.refresh()
|
|
59
|
+
router.refresh();
|
|
60
|
+
}, 500);
|
|
61
|
+
}
|
|
62
|
+
return () => {
|
|
63
|
+
if (intervalId) {
|
|
64
|
+
clearInterval(intervalId);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}, [run]);
|
|
68
|
+
return <>
|
|
69
|
+
<Container>
|
|
70
|
+
<PageHeader breadcrumbs={breadcrumbs} header={{
|
|
71
|
+
part1: 'Task Run:',
|
|
72
|
+
part2: task.name
|
|
73
|
+
}} RightButtons={RightButtons}
|
|
74
|
+
// icon={<SendIcon sx={{color: '#6435c9'}} />}
|
|
75
|
+
/>
|
|
76
|
+
<Table variant='outlined' borderAxis="bothBetween" aria-label="task runs table" size='md' sx={{
|
|
77
|
+
mt: 1,
|
|
78
|
+
maxWidth: 500,
|
|
79
|
+
'& th': {
|
|
80
|
+
height: {
|
|
81
|
+
sm: "22px",
|
|
82
|
+
md: "26px",
|
|
83
|
+
lg: "30px"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
'& td': {
|
|
87
|
+
height: {
|
|
88
|
+
sm: "23px",
|
|
89
|
+
md: "27px",
|
|
90
|
+
lg: "31px"
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}}>
|
|
94
|
+
<thead>
|
|
95
|
+
<tr>
|
|
96
|
+
{/* <th ></th> */}
|
|
97
|
+
<th>Created At</th>
|
|
98
|
+
<th>Started At</th>
|
|
99
|
+
{/* <th >ID</th> */}
|
|
100
|
+
<th>Status</th>
|
|
101
|
+
<th style={{
|
|
102
|
+
width: '58px'
|
|
103
|
+
}}>Duration</th>
|
|
104
|
+
<th>By</th>
|
|
105
|
+
{/* <th>User</th> */}
|
|
106
|
+
</tr>
|
|
107
|
+
</thead>
|
|
108
|
+
<tbody>
|
|
109
|
+
<tr key={run.id}>
|
|
110
|
+
<td>{new Date(run.created_at).toLocaleString()}</td>
|
|
111
|
+
<td>{run.started_at && new Date(run.started_at).toLocaleString()}</td>
|
|
112
|
+
|
|
113
|
+
<td><StatusChip status={run.status} /></td>
|
|
114
|
+
<td style={{
|
|
115
|
+
textAlign: 'right'
|
|
116
|
+
}}>{run.duration / 1000 || 0}s</td>
|
|
117
|
+
<td>{run.by || 'user'}</td>
|
|
118
|
+
{/* <td>{run.user}</td> */}
|
|
119
|
+
</tr>
|
|
120
|
+
</tbody>
|
|
121
|
+
</Table>
|
|
122
|
+
<Typography level="title-lg" sx={{
|
|
123
|
+
mt: 3,
|
|
124
|
+
mb: 1
|
|
125
|
+
}}>Payload:</Typography>
|
|
126
|
+
<pre>{JSON.stringify(run.inputs, null, 2)}</pre>
|
|
127
|
+
<Typography level="title-lg" sx={{
|
|
128
|
+
mt: 3,
|
|
129
|
+
mb: 1
|
|
130
|
+
}}>Logs:</Typography>
|
|
131
|
+
<Sheet sx={{
|
|
132
|
+
bgcolor: 'white'
|
|
133
|
+
}}>
|
|
134
|
+
<Table borderAxis="xBetween" size="md" variant="outlined" sx={{
|
|
135
|
+
border: '1px solid #d9dada',
|
|
136
|
+
'--TableCell-borderColor': '#ebebeb'
|
|
137
|
+
}}>
|
|
138
|
+
<tbody>
|
|
139
|
+
{logs.map((log, index) => <tr key={index}>
|
|
140
|
+
<td style={{
|
|
141
|
+
width: 90,
|
|
142
|
+
padding: 7,
|
|
143
|
+
paddingLeft: 14,
|
|
144
|
+
verticalAlign: 'top',
|
|
145
|
+
height: 'auto'
|
|
146
|
+
}}>
|
|
147
|
+
<Typography level="body-sm" fontFamily="monospace">
|
|
148
|
+
{new Date(log.created_at).toLocaleTimeString()}
|
|
149
|
+
</Typography>
|
|
150
|
+
</td>
|
|
151
|
+
<td style={{
|
|
152
|
+
padding: 7,
|
|
153
|
+
verticalAlign: 'top',
|
|
154
|
+
height: 'auto'
|
|
155
|
+
}}>
|
|
156
|
+
{log.type == 'markdown' ? <Alert variant='soft' color="primary" sx={{
|
|
157
|
+
p: 2,
|
|
158
|
+
pb: 0,
|
|
159
|
+
// bgcolor:'#e3fbe3',
|
|
160
|
+
bgcolor: '#f8ffff',
|
|
161
|
+
color: '#276f85',
|
|
162
|
+
border: '1px solid #a8d4dd',
|
|
163
|
+
'& h4, & h3,': {
|
|
164
|
+
mt: 0
|
|
165
|
+
}
|
|
166
|
+
}}>
|
|
167
|
+
<div className='' dangerouslySetInnerHTML={{
|
|
168
|
+
__html: log.content
|
|
169
|
+
}} />
|
|
170
|
+
</Alert> : log.type == 'json' ? <pre className="json-renderer" style={{
|
|
171
|
+
padding: 0,
|
|
172
|
+
margin: 0
|
|
173
|
+
}}>
|
|
174
|
+
{JSON.stringify(JSON.parse(log.content), null, 2)}
|
|
175
|
+
</pre> : log.type == 'error' ? <Alert variant='soft' color="danger" sx={{
|
|
176
|
+
p: 2,
|
|
177
|
+
pb: 0,
|
|
178
|
+
overflow: 'auto',
|
|
179
|
+
'&::-webkit-scrollbar': {
|
|
180
|
+
display: 'none'
|
|
181
|
+
},
|
|
182
|
+
scrollbarWidth: 'none',
|
|
183
|
+
// Firefox
|
|
184
|
+
msOverflowStyle: 'none',
|
|
185
|
+
// IE and Edge
|
|
186
|
+
// bgcolor:'#e3fbe3',
|
|
187
|
+
// bgcolor:'#f8ffff',
|
|
188
|
+
// color:'#276f85',
|
|
189
|
+
border: '1px solid #e0b4b4',
|
|
190
|
+
'& h4, & h3,': {
|
|
191
|
+
mt: 0
|
|
192
|
+
}
|
|
193
|
+
}}>
|
|
194
|
+
<div>
|
|
195
|
+
<h4>Error : {JSON.parse(log.content)?.message}</h4>
|
|
196
|
+
{JSON.parse(log.content)?.stack}
|
|
197
|
+
|
|
198
|
+
</div>
|
|
199
|
+
</Alert> : log.type == 'warn' ? <Alert variant='soft' color="warning" sx={{
|
|
200
|
+
py: 1,
|
|
201
|
+
my: -0.4
|
|
202
|
+
}}>
|
|
203
|
+
{log.content}
|
|
204
|
+
</Alert> : log.type == 'info' ? <Alert variant='soft' color="primary" sx={{
|
|
205
|
+
py: 1,
|
|
206
|
+
my: -0.4
|
|
207
|
+
}}>
|
|
208
|
+
{log.content}
|
|
209
|
+
</Alert> : log.type == 'danger' ? <Alert variant='soft' color="danger" sx={{
|
|
210
|
+
py: 1,
|
|
211
|
+
my: -0.4
|
|
212
|
+
}}>
|
|
213
|
+
{log.content}
|
|
214
|
+
</Alert> : log.type == 'success' ? <Alert variant='soft' color="success" sx={{
|
|
215
|
+
py: 1,
|
|
216
|
+
my: -0.4
|
|
217
|
+
}}>
|
|
218
|
+
{log.content}
|
|
219
|
+
</Alert> : <Typography level="body-sm" fontFamily="monospace">{log.content}</Typography>}
|
|
220
|
+
</td>
|
|
221
|
+
</tr>)}
|
|
222
|
+
</tbody>
|
|
223
|
+
</Table>
|
|
224
|
+
</Sheet>
|
|
225
|
+
<br />
|
|
226
|
+
{/* <pre>{JSON.stringify(task,null,2)}</pre> */}
|
|
227
|
+
{/* <pre>{JSON.stringify(run,null,2)}</pre> */}
|
|
228
|
+
</Container>
|
|
229
|
+
</>;
|
|
230
|
+
}
|
package/dist/server/app/tasks/[slug]/runs/[r_id]/_components/DropdownActions/DropdownActions.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Menu, MenuItem, MenuButton, Dropdown } from '@mui/joy';
|
|
4
|
+
import * as React from 'react';
|
|
5
|
+
import Button from '@mui/joy/Button';
|
|
6
|
+
// import Divider from '@mui/joy/Divider';
|
|
7
|
+
|
|
8
|
+
import { deleteRun } from "./action";
|
|
9
|
+
export default function DropdownActions({
|
|
10
|
+
run
|
|
11
|
+
}) {
|
|
12
|
+
async function onClickCreateVersion() {}
|
|
13
|
+
async function onClickCopyRerunAsCurl() {
|
|
14
|
+
let href = window.location.href;
|
|
15
|
+
let baseUrl = href.split('/runs/')[0].replace('/tasks/', '/api/tasks/');
|
|
16
|
+
|
|
17
|
+
// Convert input object to URL query parameters
|
|
18
|
+
const queryParams = new URLSearchParams();
|
|
19
|
+
Object.entries(run?.inputs).forEach(([key, value]) => {
|
|
20
|
+
queryParams.append(key, value);
|
|
21
|
+
});
|
|
22
|
+
const curlCommand = `curl -X POST "${baseUrl}?${queryParams.toString()}"`;
|
|
23
|
+
await navigator.clipboard.writeText(curlCommand);
|
|
24
|
+
}
|
|
25
|
+
return <>
|
|
26
|
+
<Dropdown size='sm'>
|
|
27
|
+
<MenuButton aria-label="master-data more options" variant="outlined" color="primary" size="sm" sx={{
|
|
28
|
+
// Add these styles to match IconButton
|
|
29
|
+
aspectRatio: '1',
|
|
30
|
+
// borderRadius: '50%',
|
|
31
|
+
p: 0.5,
|
|
32
|
+
display: 'inline-flex',
|
|
33
|
+
alignItems: 'center',
|
|
34
|
+
justifyContent: 'center'
|
|
35
|
+
}} onClick={event => {
|
|
36
|
+
event.preventDefault();
|
|
37
|
+
event.stopPropagation();
|
|
38
|
+
}}><i class="fa-solid fa-ellipsis-vertical fa-lg"></i></MenuButton>
|
|
39
|
+
<Menu placement='bottom-end'>
|
|
40
|
+
<MenuItem disabled onClick={onClickCreateVersion}>Rerun job</MenuItem>
|
|
41
|
+
<MenuItem onClick={onClickCopyRerunAsCurl}>Copy rerun as curl</MenuItem>
|
|
42
|
+
{/* <MenuItem color='danger' onClick={()=>{setOpen(true);}}>Delete Part</MenuItem> */}
|
|
43
|
+
</Menu>
|
|
44
|
+
</Dropdown>
|
|
45
|
+
</>;
|
|
46
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
// import analytics from '@/lib/analytics';
|
|
4
|
+
// import db from '@/database';
|
|
5
|
+
|
|
6
|
+
// export async function deleteRun(run) {
|
|
7
|
+
// // const user = await loginRequired();
|
|
8
|
+
// // analytics.track({event: 'part_delete', user: user.id, org: part.org});
|
|
9
|
+
|
|
10
|
+
// // Check for existing versions
|
|
11
|
+
// const versions = await db.PartVersions.findAll({
|
|
12
|
+
// where: { quote: part.id},
|
|
13
|
+
// raw: true
|
|
14
|
+
// });
|
|
15
|
+
|
|
16
|
+
// if (versions.length) {
|
|
17
|
+
// return {
|
|
18
|
+
// status: 'failed',
|
|
19
|
+
// message: 'Part contains versions. Please delete the versions first.'
|
|
20
|
+
// };
|
|
21
|
+
// } else {
|
|
22
|
+
// try {
|
|
23
|
+
// await db.Parts.destroy({
|
|
24
|
+
// where: { id: part.id }
|
|
25
|
+
// });
|
|
26
|
+
|
|
27
|
+
// return {
|
|
28
|
+
// status: 'done',
|
|
29
|
+
// message: 'Part deleted.'
|
|
30
|
+
// };
|
|
31
|
+
// } catch (error) {
|
|
32
|
+
// throw error;
|
|
33
|
+
// }
|
|
34
|
+
// }
|
|
35
|
+
// }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import getTaskDetails from "../../../../../lib/getTaskDetails";
|
|
2
|
+
import ViewRun from "./ViewRun";
|
|
3
|
+
import async from 'async';
|
|
4
|
+
import microlightDB from "../../../../../database/microlight";
|
|
5
|
+
import { notFound } from "next/navigation";
|
|
6
|
+
export default async function Page({
|
|
7
|
+
params,
|
|
8
|
+
searchParams
|
|
9
|
+
}) {
|
|
10
|
+
params = await params;
|
|
11
|
+
searchParams = await searchParams;
|
|
12
|
+
const workflow = {
|
|
13
|
+
getTask: async function () {
|
|
14
|
+
const task = await getTaskDetails({
|
|
15
|
+
params
|
|
16
|
+
});
|
|
17
|
+
delete task.fn;
|
|
18
|
+
return task;
|
|
19
|
+
},
|
|
20
|
+
getRun: async function () {
|
|
21
|
+
let run = await microlightDB.Runs.findOne({
|
|
22
|
+
where: {
|
|
23
|
+
task: params.slug,
|
|
24
|
+
id: params.r_id
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (!run) return notFound();
|
|
28
|
+
return run.toJSON();
|
|
29
|
+
},
|
|
30
|
+
getLogs: async function () {
|
|
31
|
+
let logs = await microlightDB.Logs.findAll({
|
|
32
|
+
where: {
|
|
33
|
+
run: params.r_id
|
|
34
|
+
},
|
|
35
|
+
order: [['created_at', 'ASC']]
|
|
36
|
+
});
|
|
37
|
+
logs = logs.map(l => l.toJSON());
|
|
38
|
+
return logs;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
let results = await async.auto(workflow);
|
|
42
|
+
return <ViewRun params={params} searchParams={searchParams} task={results.getTask} run={results.getRun} logs={results.getLogs} />;
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* there is an issue with mui icons.
|
|
3
|
+
* manually setting the icons.
|
|
4
|
+
* the svgs of icons are picked up from fontawesome
|
|
5
|
+
* https://fontawesome.com/search?ic=free
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import { AspectRatio } from '@mui/joy';
|
|
9
|
+
export default function Icon({
|
|
10
|
+
color,
|
|
11
|
+
icon
|
|
12
|
+
}) {
|
|
13
|
+
return <>
|
|
14
|
+
<AspectRatio ratio="1" sx={{
|
|
15
|
+
width: 20
|
|
16
|
+
}} variant="plain">
|
|
17
|
+
{icon == 'send' && <svg fill={color} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6L284 427.7l-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1S160 493.2 160 480l0-83.6c0-4 1.5-7.8 4.2-10.8L331.8 202.8c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7L106 360.8 17.7 316.6C7.1 311.3 .3 300.7 0 288.9s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z" /></svg>}
|
|
18
|
+
{icon == 'folder' && <svg fill={color} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M0 96C0 60.7 28.7 32 64 32l132.1 0c19.1 0 37.4 7.6 50.9 21.1L289.9 96 448 96c35.3 0 64 28.7 64 64l0 256c0 35.3-28.7 64-64 64L64 480c-35.3 0-64-28.7-64-64L0 96zM64 80c-8.8 0-16 7.2-16 16l0 320c0 8.8 7.2 16 16 16l384 0c8.8 0 16-7.2 16-16l0-256c0-8.8-7.2-16-16-16l-161.4 0c-10.6 0-20.8-4.2-28.3-11.7L213.1 87c-4.5-4.5-10.6-7-17-7L64 80z" /></svg>}
|
|
19
|
+
{icon == 'ellipsis-vertical' && <svg fill={color} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 512"><path d="M64 360a56 56 0 1 0 0 112 56 56 0 1 0 0-112zm0-160a56 56 0 1 0 0 112 56 56 0 1 0 0-112zM120 96A56 56 0 1 0 8 96a56 56 0 1 0 112 0z" /></svg>}
|
|
20
|
+
</AspectRatio>
|
|
21
|
+
</>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Link as JoyLink } from '@mui/joy';
|
|
5
|
+
import NextLink from 'next/link';
|
|
6
|
+
import PropTypes from 'prop-types';
|
|
7
|
+
export default function Link({
|
|
8
|
+
href = '/',
|
|
9
|
+
sx = {},
|
|
10
|
+
target = '_self',
|
|
11
|
+
onClick = () => {},
|
|
12
|
+
children,
|
|
13
|
+
Component = null
|
|
14
|
+
}) {
|
|
15
|
+
if (Component) {
|
|
16
|
+
return <NextLink href={href} target={target} passHref>
|
|
17
|
+
<Component onClick={e => {
|
|
18
|
+
if (onClick) {
|
|
19
|
+
onClick(e);
|
|
20
|
+
}
|
|
21
|
+
}} sx={sx}>
|
|
22
|
+
{children}
|
|
23
|
+
</Component>
|
|
24
|
+
</NextLink>;
|
|
25
|
+
}
|
|
26
|
+
const defaultSx = {
|
|
27
|
+
backgroundColor: "transparent",
|
|
28
|
+
"&:hover": {
|
|
29
|
+
backgroundColor: "none"
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return <JoyLink color="neutral" component={NextLink} underline="none" target={target} href={href} sx={{
|
|
33
|
+
...defaultSx,
|
|
34
|
+
...sx
|
|
35
|
+
}} onClick={e => {
|
|
36
|
+
if (onClick) {
|
|
37
|
+
onClick(e);
|
|
38
|
+
}
|
|
39
|
+
}}>
|
|
40
|
+
{children}
|
|
41
|
+
</JoyLink>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// For Storybook Documentation
|
|
45
|
+
Link.propTypes = {
|
|
46
|
+
children: PropTypes.node.isRequired,
|
|
47
|
+
sx: PropTypes.object,
|
|
48
|
+
onClick: PropTypes.func,
|
|
49
|
+
Component: PropTypes.elementType,
|
|
50
|
+
href: PropTypes.string.isRequired,
|
|
51
|
+
target: PropTypes.oneOf(['_self', '_blank', '_parent', '_top'])
|
|
52
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { FormControl, FormLabel, Input, Select, Option } from "@mui/joy";
|
|
2
|
+
export default function MLInput({
|
|
3
|
+
def,
|
|
4
|
+
slug,
|
|
5
|
+
searchParams
|
|
6
|
+
}) {
|
|
7
|
+
return <>
|
|
8
|
+
<FormControl required={def.required} sx={{
|
|
9
|
+
mb: 2
|
|
10
|
+
}}>
|
|
11
|
+
<FormLabel sx={{
|
|
12
|
+
mb: 0.25
|
|
13
|
+
}}>{def.name}:</FormLabel>
|
|
14
|
+
{['string', 'number', 'date'].includes(def.type) && <Input size="sm" name={slug} placeholder={def.placeholder} defaultValue={searchParams[slug] || def.default || ""} type={def.type} />}
|
|
15
|
+
{def.type === 'file' &&
|
|
16
|
+
// Placeholder for future file input implementation
|
|
17
|
+
<Input {...commonProps} type="file"
|
|
18
|
+
// Add any file-specific props here
|
|
19
|
+
/>}
|
|
20
|
+
{def.type === 'dropdown' && <Select size="sm" name={slug} placeholder={def.placeholder} defaultValue={searchParams[slug] || def.default || ""}>
|
|
21
|
+
{def.options?.map(option => <Option key={option.value} value={option.value}>
|
|
22
|
+
{option.label || option.value}
|
|
23
|
+
</Option>)}
|
|
24
|
+
</Select>}
|
|
25
|
+
</FormControl>
|
|
26
|
+
{/* string input - {slug} <br/> */}
|
|
27
|
+
{/* <pre>{JSON.stringify(def,null,2)}</pre> */}
|
|
28
|
+
</>;
|
|
29
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
// import Logo from '../Logo';
|
|
4
|
+
import { Box, Sheet } from '@mui/joy';
|
|
5
|
+
export default function Navbar({
|
|
6
|
+
user,
|
|
7
|
+
signOut
|
|
8
|
+
}) {
|
|
9
|
+
return <Sheet component="nav" sx={{
|
|
10
|
+
px: 1,
|
|
11
|
+
// py: 0.5,
|
|
12
|
+
display: 'flex',
|
|
13
|
+
alignItems: 'center',
|
|
14
|
+
justifyContent: 'space-between',
|
|
15
|
+
// boxShadow: 'sm',
|
|
16
|
+
borderBottom: '1px solid',
|
|
17
|
+
borderColor: 'divider',
|
|
18
|
+
position: 'fixed',
|
|
19
|
+
top: 0,
|
|
20
|
+
left: 0,
|
|
21
|
+
right: 0,
|
|
22
|
+
height: '40px',
|
|
23
|
+
zIndex: 1000,
|
|
24
|
+
bgcolor: 'background.surface'
|
|
25
|
+
// bgcolor: 'white',
|
|
26
|
+
}}>
|
|
27
|
+
<Box sx={{
|
|
28
|
+
display: 'flex',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
gap: 1
|
|
31
|
+
}}>
|
|
32
|
+
{/* <Logo offering='Transactions' /> */}
|
|
33
|
+
Microlight
|
|
34
|
+
</Box>
|
|
35
|
+
|
|
36
|
+
</Sheet>;
|
|
37
|
+
}
|
|
38
|
+
;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Navbar from "./Navbar";
|
|
2
|
+
import { Box } from "@mui/joy";
|
|
3
|
+
export default function NavbarContainer({
|
|
4
|
+
children
|
|
5
|
+
}) {
|
|
6
|
+
return <>
|
|
7
|
+
<Navbar />
|
|
8
|
+
<Box component="main" sx={{
|
|
9
|
+
pt: '40px',
|
|
10
|
+
display: 'flex'
|
|
11
|
+
}}>
|
|
12
|
+
<Box sx={{
|
|
13
|
+
flex: 1,
|
|
14
|
+
px: {
|
|
15
|
+
xs: 1,
|
|
16
|
+
sm: 1.5
|
|
17
|
+
},
|
|
18
|
+
pt: 0.5,
|
|
19
|
+
pb: 0,
|
|
20
|
+
minWidth: 0
|
|
21
|
+
}}>
|
|
22
|
+
{children}
|
|
23
|
+
</Box>
|
|
24
|
+
</Box>
|
|
25
|
+
</>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Box, Typography, Breadcrumbs } from '@mui/joy';
|
|
5
|
+
import { Link as JoyLink } from '@mui/joy';
|
|
6
|
+
import NextLink from 'next/link';
|
|
7
|
+
|
|
8
|
+
// Create a basic link component in switchless and import it here
|
|
9
|
+
const BreadcrumbLink = ({
|
|
10
|
+
href,
|
|
11
|
+
children
|
|
12
|
+
}) => {
|
|
13
|
+
return <JoyLink color="primary" component={NextLink} href={href}>
|
|
14
|
+
{children}
|
|
15
|
+
</JoyLink>;
|
|
16
|
+
};
|
|
17
|
+
const BreadcrumbsCustom = function ({
|
|
18
|
+
breadcrumbs
|
|
19
|
+
}) {
|
|
20
|
+
return <Breadcrumbs separator="›" aria-label="breadcrumbs" sx={{
|
|
21
|
+
p: 0
|
|
22
|
+
}}>
|
|
23
|
+
{breadcrumbs.map((b, index) => <React.Fragment key={index}>
|
|
24
|
+
{b.href ? <BreadcrumbLink key={b.href} href={b.href}>
|
|
25
|
+
{b.text}
|
|
26
|
+
</BreadcrumbLink> : <Typography key={b.text}>{b.text}</Typography>}
|
|
27
|
+
</React.Fragment>)}
|
|
28
|
+
</Breadcrumbs>;
|
|
29
|
+
};
|
|
30
|
+
export default function PageHeader({
|
|
31
|
+
header = "PageHeader",
|
|
32
|
+
RightButtons = null,
|
|
33
|
+
level = 'h3',
|
|
34
|
+
headerLevel = null,
|
|
35
|
+
breadcrumbs = null
|
|
36
|
+
}) {
|
|
37
|
+
if (headerLevel) level = headerLevel;
|
|
38
|
+
const renderHeader = () => {
|
|
39
|
+
if (React.isValidElement(header)) {
|
|
40
|
+
return header; // Return React component as is
|
|
41
|
+
} else if (typeof header === 'string') {
|
|
42
|
+
return <Typography level={level}>{header}</Typography>;
|
|
43
|
+
} else if (typeof header === 'object') {
|
|
44
|
+
const headerParts = Object.values(header);
|
|
45
|
+
return <Typography level={level}>
|
|
46
|
+
{headerParts.length === 1 ? <span>
|
|
47
|
+
{headerParts[0]}
|
|
48
|
+
</span> : headerParts.map((part, index) => <span key={index} style={{
|
|
49
|
+
opacity: index === 1 ? 1 : 0.5
|
|
50
|
+
}}>
|
|
51
|
+
{part}{index < headerParts.length - 1 && ' '}
|
|
52
|
+
</span>)}
|
|
53
|
+
</Typography>;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
return <Box data-cy='page-header' sx={{
|
|
58
|
+
display: 'flex',
|
|
59
|
+
flexDirection: {
|
|
60
|
+
xs: 'column',
|
|
61
|
+
sm: 'row'
|
|
62
|
+
},
|
|
63
|
+
alignItems: {
|
|
64
|
+
xs: 'flex-start',
|
|
65
|
+
sm: 'center'
|
|
66
|
+
},
|
|
67
|
+
gap: {
|
|
68
|
+
xs: 1,
|
|
69
|
+
sm: 1
|
|
70
|
+
},
|
|
71
|
+
pt: 0.5
|
|
72
|
+
}}>
|
|
73
|
+
<Box sx={{
|
|
74
|
+
flexGrow: 1
|
|
75
|
+
}}>
|
|
76
|
+
{breadcrumbs && <BreadcrumbsCustom breadcrumbs={breadcrumbs} />}
|
|
77
|
+
{renderHeader()}
|
|
78
|
+
</Box>
|
|
79
|
+
{RightButtons && <Box sx={{
|
|
80
|
+
flexGrow: 0,
|
|
81
|
+
width: 'auto',
|
|
82
|
+
margin: "auto 0"
|
|
83
|
+
}}>
|
|
84
|
+
{typeof RightButtons === 'function' ? <RightButtons /> : RightButtons}
|
|
85
|
+
</Box>}
|
|
86
|
+
</Box>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Chip } from '@mui/joy';
|
|
2
|
+
function chipStatusColor(status) {
|
|
3
|
+
if (status === 'complete') return 'success';else if (status === 'pending') return 'warning';else if (status === 'running') return 'primary';else return 'danger';
|
|
4
|
+
}
|
|
5
|
+
export default function StatusChip({
|
|
6
|
+
status
|
|
7
|
+
}) {
|
|
8
|
+
return <Chip variant="soft" color={chipStatusColor(status)} size="sm">
|
|
9
|
+
{status || 'pending'}
|
|
10
|
+
</Chip>;
|
|
11
|
+
}
|