feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
@@ -0,0 +1,4 @@
import React from 'react';
export declare const Button: ({ children, className, isActive, ...rest }: React.ClassAttributes<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement> & {
isActive?: boolean | undefined;
}) => JSX.Element;
@@ -0,0 +1,12 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Button = void 0;
const react_1 = __importDefault(require("react"));
const Button_module_css_1 = __importDefault(require("./Button.module.css"));
const classnames_1 = __importDefault(require("classnames"));
const Button = ({ children, className, isActive = false, ...rest }) => (react_1.default.createElement("button", Object.assign({ type: "button" }, rest, { className: classnames_1.default(Button_module_css_1.default.button, className, { [Button_module_css_1.default.isActive]: isActive }) }), children));
exports.Button = Button;
//# sourceMappingURL=Button.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Button.js","sourceRoot":"","sources":["../../../../../src/ui/components/JobCard/Button/Button.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AACzB,4EAAmC;AACnC,4DAA2B;AAEpB,MAAM,MAAM,GAAG,CAAC,EACrB,QAAQ,EACR,SAAS,EACT,QAAQ,GAAG,KAAK,EAChB,GAAG,IAAI,EAIiB,EAAE,EAAE,CAAC,CAC7B,wDACE,IAAI,EAAC,QAAQ,IACT,IAAI,IACR,SAAS,EAAE,oBAAE,CAAC,2BAAC,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,CAAC,2BAAC,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC,KAE7D,QAAQ,CACF,CACV,CAAA;AAhBY,QAAA,MAAM,UAgBlB"}
@@ -0,0 +1,12 @@
/// <reference types="react" />
import { AppJob } from '../../../../@types/app';
import { Status } from '../../constants';
interface DetailsProps {
job: AppJob;
status: Status;
actions: {
getJobLogs: () => Promise<string[]>;
};
}
export declare const Details: ({ status, job, actions }: DetailsProps) => JSX.Element | null;
export {};
@@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Details = void 0;
const react_1 = __importDefault(require("react"));
const useDetailsTabs_1 = require("../../../hooks/useDetailsTabs");
const Button_1 = require("../Button/Button");
const Details_module_css_1 = __importDefault(require("./Details.module.css"));
const DetailsContent_1 = require("./DetailsContent/DetailsContent");
const Details = ({ status, job, actions }) => {
const { tabs, selectedTab } = useDetailsTabs_1.useDetailsTabs(status);
if (tabs.length === 0) {
return null;
}
return (react_1.default.createElement("div", { className: Details_module_css_1.default.details },
react_1.default.createElement("ul", { className: Details_module_css_1.default.tabActions }, tabs.map((tab) => (react_1.default.createElement("li", { key: tab.title },
react_1.default.createElement(Button_1.Button, { onClick: tab.selectTab, isActive: tab.isActive }, tab.title))))),
react_1.default.createElement("div", { className: Details_module_css_1.default.tabContent },
react_1.default.createElement("div", null,
react_1.default.createElement(DetailsContent_1.DetailsContent, { selectedTab: selectedTab, job: job, actions: actions })))));
};
exports.Details = Details;
//# sourceMappingURL=Details.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Details.js","sourceRoot":"","sources":["../../../../../src/ui/components/JobCard/Details/Details.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AAEzB,kEAA8D;AAE9D,6CAAyC;AACzC,8EAAoC;AACpC,oEAAgE;AAQzD,MAAM,OAAO,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAgB,EAAE,EAAE;IAChE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,+BAAc,CAAC,MAAM,CAAC,CAAA;IAEpD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACrB,OAAO,IAAI,CAAA;KACZ;IAED,OAAO,CACL,uCAAK,SAAS,EAAE,4BAAC,CAAC,OAAO;QACvB,sCAAI,SAAS,EAAE,4BAAC,CAAC,UAAU,IACxB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CACjB,sCAAI,GAAG,EAAE,GAAG,CAAC,KAAK;YAChB,8BAAC,eAAM,IAAC,OAAO,EAAE,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,IACnD,GAAG,CAAC,KAAK,CACH,CACN,CACN,CAAC,CACC;QACL,uCAAK,SAAS,EAAE,4BAAC,CAAC,UAAU;YAC1B;gBACE,8BAAC,+BAAc,IACb,WAAW,EAAE,WAAW,EACxB,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,OAAO,GAChB,CACE,CACF,CACF,CACP,CAAA;AACH,CAAC,CAAA;AA7BY,QAAA,OAAO,WA6BnB"}
@@ -0,0 +1,12 @@
/// <reference types="react" />
import { AppJob } from '../../../../../@types/app';
import { TabsType } from '../../../../hooks/useDetailsTabs';
interface DetailsContentProps {
job: AppJob;
selectedTab: TabsType;
actions: {
getJobLogs: () => Promise<string[]>;
};
}
export declare const DetailsContent: ({ selectedTab, job: { stacktrace, data, returnValue, opts, failedReason }, actions, }: DetailsContentProps) => JSX.Element | null;
export {};
@@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DetailsContent = void 0;
const react_1 = __importDefault(require("react"));
const Highlight_1 = require("../../../Highlight/Highlight");
const JobLogs_1 = require("./JobLogs/JobLogs");
const DetailsContent = ({ selectedTab, job: { stacktrace, data, returnValue, opts, failedReason }, actions, }) => {
switch (selectedTab) {
case 'Data':
return (react_1.default.createElement(Highlight_1.Highlight, { language: "json" }, JSON.stringify({ data, returnValue }, null, 2)));
case 'Options':
return (react_1.default.createElement(Highlight_1.Highlight, { language: "json" }, JSON.stringify(opts, null, 2)));
case 'Error':
return (react_1.default.createElement(react_1.default.Fragment, null, stacktrace.length === 0 ? (react_1.default.createElement("div", { className: "error" }, !!failedReason ? failedReason : 'NA')) : (react_1.default.createElement(Highlight_1.Highlight, { language: "stacktrace", key: "stacktrace" }, stacktrace.join('\n')))));
case 'Logs':
return react_1.default.createElement(JobLogs_1.JobLogs, { actions: actions });
default:
return null;
}
};
exports.DetailsContent = DetailsContent;
//# sourceMappingURL=DetailsContent.js.map
@@ -0,0 +1 @@
{"version":3,"file":"DetailsContent.js","sourceRoot":"","sources":["../../../../../../src/ui/components/JobCard/Details/DetailsContent/DetailsContent.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AAGzB,4DAAwD;AACxD,+CAA2C;AAUpC,MAAM,cAAc,GAAG,CAAC,EAC7B,WAAW,EACX,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,YAAY,EAAE,EAC1D,OAAO,GACa,EAAE,EAAE;IACxB,QAAQ,WAAW,EAAE;QACnB,KAAK,MAAM;YACT,OAAO,CACL,8BAAC,qBAAS,IAAC,QAAQ,EAAC,MAAM,IACvB,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CACrC,CACb,CAAA;QACH,KAAK,SAAS;YACZ,OAAO,CACL,8BAAC,qBAAS,IAAC,QAAQ,EAAC,MAAM,IAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAa,CACvE,CAAA;QACH,KAAK,OAAO;YACV,OAAO,CACL,8DACG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACzB,uCAAK,SAAS,EAAC,OAAO,IAAE,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAO,CACpE,CAAC,CAAC,CAAC,CACF,8BAAC,qBAAS,IAAC,QAAQ,EAAC,YAAY,EAAC,GAAG,EAAC,YAAY,IAC9C,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CACb,CACA,CACJ,CAAA;QACH,KAAK,MAAM;YACT,OAAO,8BAAC,iBAAO,IAAC,OAAO,EAAE,OAAO,GAAI,CAAA;QACtC;YACE,OAAO,IAAI,CAAA;KACd;AACH,CAAC,CAAA;AAjCY,QAAA,cAAc,kBAiC1B"}
@@ -0,0 +1,8 @@
/// <reference types="react" />
interface JobLogsProps {
actions: {
getJobLogs: () => Promise<string[]>;
};
}
export declare const JobLogs: ({ actions }: JobLogsProps) => JSX.Element | null;
export {};
@@ -0,0 +1,43 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JobLogs = void 0;
const react_1 = __importStar(require("react"));
const JobLogs_module_css_1 = __importDefault(require("./JobLogs.module.css"));
const JobLogs = ({ actions }) => {
const [logs, setLogs] = react_1.useState([]);
react_1.useEffect(() => {
let mounted = true;
actions.getJobLogs().then((logs) => mounted && setLogs(logs));
return () => {
mounted = false;
};
}, []);
if (!Array.isArray(logs) || !logs.length) {
return null;
}
return (react_1.default.createElement("ul", { className: JobLogs_module_css_1.default.jobLogs }, logs.map((log, idx) => (react_1.default.createElement("li", { key: idx }, log)))));
};
exports.JobLogs = JobLogs;
//# sourceMappingURL=JobLogs.js.map
@@ -0,0 +1 @@
{"version":3,"file":"JobLogs.js","sourceRoot":"","sources":["../../../../../../../src/ui/components/JobCard/Details/DetailsContent/JobLogs/JobLogs.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+CAAkD;AAClD,8EAAoC;AAQ7B,MAAM,OAAO,GAAG,CAAC,EAAE,OAAO,EAAgB,EAAE,EAAE;IACnD,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,gBAAQ,CAAW,EAAE,CAAC,CAAA;IAE9C,iBAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,GAAG,IAAI,CAAA;QAClB,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;QAC7D,OAAO,GAAG,EAAE;YACV,OAAO,GAAG,KAAK,CAAA;QACjB,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;QACxC,OAAO,IAAI,CAAA;KACZ;IAED,OAAO,CACL,sCAAI,SAAS,EAAE,4BAAC,CAAC,OAAO,IACrB,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,CACtB,sCAAI,GAAG,EAAE,GAAG,IAAG,GAAG,CAAM,CACzB,CAAC,CACC,CACN,CAAA;AACH,CAAC,CAAA;AAtBY,QAAA,OAAO,WAsBnB"}
@@ -0,0 +1,12 @@
/// <reference types="react" />
import { Status } from '../../constants';
interface JobActionsProps {
status: Status;
actions: {
promoteJob: () => Promise<void>;
retryJob: () => Promise<void>;
cleanJob: () => Promise<void>;
};
}
export declare const JobActions: ({ actions, status }: JobActionsProps) => JSX.Element | null;
export {};
@@ -0,0 +1,37 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JobActions = void 0;
const react_1 = __importDefault(require("react"));
const constants_1 = require("../../constants");
const Promote_1 = require("../../Icons/Promote");
const Retry_1 = require("../../Icons/Retry");
const Trash_1 = require("../../Icons/Trash");
const Tooltip_1 = require("../../Tooltip/Tooltip");
const Button_1 = require("../Button/Button");
const JobActions_module_css_1 = __importDefault(require("./JobActions.module.css"));
const buttonTypes = {
promote: { title: 'Promote', Icon: Promote_1.PromoteIcon, actionKey: 'promoteJob' },
clean: { title: 'Clean', Icon: Trash_1.TrashIcon, actionKey: 'cleanJob' },
retry: { title: 'Retry', Icon: Retry_1.RetryIcon, actionKey: 'retryJob' },
};
const statusToButtonsMap = {
[constants_1.STATUSES.failed]: [buttonTypes.retry, buttonTypes.clean],
[constants_1.STATUSES.delayed]: [buttonTypes.promote, buttonTypes.clean],
[constants_1.STATUSES.completed]: [buttonTypes.clean],
[constants_1.STATUSES.waiting]: [buttonTypes.clean],
};
const JobActions = ({ actions, status }) => {
const buttons = statusToButtonsMap[status];
if (!buttons) {
return null;
}
return (react_1.default.createElement("ul", { className: JobActions_module_css_1.default.jobActions }, buttons.map((type) => (react_1.default.createElement("li", { key: type.title },
react_1.default.createElement(Tooltip_1.Tooltip, { title: type.title },
react_1.default.createElement(Button_1.Button, { onClick: actions[type.actionKey], className: JobActions_module_css_1.default.button },
react_1.default.createElement(type.Icon, null))))))));
};
exports.JobActions = JobActions;
//# sourceMappingURL=JobActions.js.map
@@ -0,0 +1 @@
{"version":3,"file":"JobActions.js","sourceRoot":"","sources":["../../../../../src/ui/components/JobCard/JobActions/JobActions.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AACzB,+CAAkD;AAClD,iDAAiD;AACjD,6CAA6C;AAC7C,6CAA6C;AAC7C,mDAA+C;AAC/C,6CAAyC;AACzC,oFAAuC;AAiBvC,MAAM,WAAW,GAA+B;IAC9C,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,qBAAW,EAAE,SAAS,EAAE,YAAY,EAAE;IACzE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAS,EAAE,SAAS,EAAE,UAAU,EAAE;IACjE,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAS,EAAE,SAAS,EAAE,UAAU,EAAE;CAClE,CAAA;AAED,MAAM,kBAAkB,GAAiC;IACvD,CAAC,oBAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;IACzD,CAAC,oBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC;IAC5D,CAAC,oBAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC;IACzC,CAAC,oBAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC;CACxC,CAAA;AAEM,MAAM,UAAU,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAmB,EAAE,EAAE;IACjE,MAAM,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;IAC1C,IAAI,CAAC,OAAO,EAAE;QACZ,OAAO,IAAI,CAAA;KACZ;IACD,OAAO,CACL,sCAAI,SAAS,EAAE,+BAAC,CAAC,UAAU,IACxB,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACrB,sCAAI,GAAG,EAAE,IAAI,CAAC,KAAK;QACjB,8BAAC,iBAAO,IAAC,KAAK,EAAE,IAAI,CAAC,KAAK;YACxB,8BAAC,eAAM,IAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,+BAAC,CAAC,MAAM;gBAC3D,8BAAC,IAAI,CAAC,IAAI,OAAG,CACN,CACD,CACP,CACN,CAAC,CACC,CACN,CAAA;AACH,CAAC,CAAA;AAlBY,QAAA,UAAU,cAkBtB"}
@@ -0,0 +1,16 @@
/// <reference types="react" />
import { AppJob } from '../../../@types/app';
import { Status } from '../constants';
interface JobCardProps {
job: AppJob;
status: Status;
readOnlyMode: boolean;
actions: {
promoteJob: () => Promise<void>;
retryJob: () => Promise<void>;
cleanJob: () => Promise<void>;
getJobLogs: () => Promise<string[]>;
};
}
export declare const JobCard: ({ job, status, actions, readOnlyMode, }: JobCardProps) => JSX.Element;
export {};
+31
View File
@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.JobCard = void 0;
const react_1 = __importDefault(require("react"));
const Details_1 = require("./Details/Details");
const JobActions_1 = require("./JobActions/JobActions");
const JobCard_module_css_1 = __importDefault(require("./JobCard.module.css"));
const Progress_1 = require("./Progress/Progress");
const Timeline_1 = require("./Timeline/Timeline");
const JobCard = ({ job, status, actions, readOnlyMode, }) => (react_1.default.createElement("div", { className: JobCard_module_css_1.default.card },
react_1.default.createElement("div", { className: JobCard_module_css_1.default.sideInfo },
react_1.default.createElement("span", { title: `#${job.id}` },
"#",
job.id),
react_1.default.createElement(Timeline_1.Timeline, { job: job, status: status })),
react_1.default.createElement("div", { className: JobCard_module_css_1.default.contentWrapper },
react_1.default.createElement("div", { className: JobCard_module_css_1.default.title },
react_1.default.createElement("h4", null,
job.name,
job.attempts > 0 && react_1.default.createElement("span", null,
"attempt #",
job.attempts + 1)),
!readOnlyMode && react_1.default.createElement(JobActions_1.JobActions, { status: status, actions: actions })),
react_1.default.createElement("div", { className: JobCard_module_css_1.default.content },
react_1.default.createElement(Details_1.Details, { status: status, job: job, actions: actions }),
typeof job.progress === 'number' && (react_1.default.createElement(Progress_1.Progress, { percentage: job.progress, status: status, className: JobCard_module_css_1.default.progress }))))));
exports.JobCard = JobCard;
//# sourceMappingURL=JobCard.js.map
@@ -0,0 +1 @@
{"version":3,"file":"JobCard.js","sourceRoot":"","sources":["../../../../src/ui/components/JobCard/JobCard.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AAGzB,+CAA2C;AAC3C,wDAAoD;AACpD,8EAAoC;AACpC,kDAA8C;AAC9C,kDAA8C;AAcvC,MAAM,OAAO,GAAG,CAAC,EACtB,GAAG,EACH,MAAM,EACN,OAAO,EACP,YAAY,GACC,EAAE,EAAE,CAAC,CAClB,uCAAK,SAAS,EAAE,4BAAC,CAAC,IAAI;IACpB,uCAAK,SAAS,EAAE,4BAAC,CAAC,QAAQ;QACxB,wCAAM,KAAK,EAAE,IAAI,GAAG,CAAC,EAAE,EAAE;;YAAI,GAAG,CAAC,EAAE,CAAQ;QAC3C,8BAAC,mBAAQ,IAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAI,CAClC;IACN,uCAAK,SAAS,EAAE,4BAAC,CAAC,cAAc;QAC9B,uCAAK,SAAS,EAAE,4BAAC,CAAC,KAAK;YACrB;gBACG,GAAG,CAAC,IAAI;gBACR,GAAG,CAAC,QAAQ,GAAG,CAAC,IAAI;;oBAAgB,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAQ,CAC1D;YACJ,CAAC,YAAY,IAAI,8BAAC,uBAAU,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAI,CAC9D;QACN,uCAAK,SAAS,EAAE,4BAAC,CAAC,OAAO;YACvB,8BAAC,iBAAO,IAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAI;YACtD,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,CACnC,8BAAC,mBAAQ,IACP,UAAU,EAAE,GAAG,CAAC,QAAQ,EACxB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,4BAAC,CAAC,QAAQ,GACrB,CACH,CACG,CACF,CACF,CACP,CAAA;AA/BY,QAAA,OAAO,WA+BnB"}
@@ -0,0 +1,7 @@
/// <reference types="react" />
import { Status } from '../../constants';
export declare const Progress: ({ percentage, status, className, }: {
percentage: number;
status: Status;
className?: string | undefined;
}) => JSX.Element;
@@ -0,0 +1,19 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Progress = void 0;
const react_1 = __importDefault(require("react"));
const Progress_module_css_1 = __importDefault(require("./Progress.module.css"));
const classnames_1 = __importDefault(require("classnames"));
const Progress = ({ percentage, status, className, }) => {
return (react_1.default.createElement("svg", { className: classnames_1.default(Progress_module_css_1.default.progress, className), viewBox: "0 0 148 148" },
react_1.default.createElement("circle", { cx: "70", cy: "70", r: "70", fill: "none", stroke: "#EDF2F7", strokeWidth: "8", strokeLinecap: "round", style: { transform: 'translate(4px, 4px)' } }),
react_1.default.createElement("circle", { cx: "70", cy: "70", r: "70", fill: "none", stroke: status === 'failed' ? '#F56565' : '#48BB78', strokeWidth: "8", strokeLinecap: "round", strokeDasharray: "600", strokeDashoffset: 600 - ((600 - 160) * percentage) / 100, style: { transform: 'translate(4px, -4px) rotate(-90deg)' } }),
react_1.default.createElement("text", { textAnchor: "middle", x: "74", y: "88" },
percentage,
"%")));
};
exports.Progress = Progress;
//# sourceMappingURL=Progress.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Progress.js","sourceRoot":"","sources":["../../../../../src/ui/components/JobCard/Progress/Progress.tsx"],"names":[],"mappings":";;;;;;AAAA,kDAAyB;AAEzB,gFAAqC;AACrC,4DAA2B;AAEpB,MAAM,QAAQ,GAAG,CAAC,EACvB,UAAU,EACV,MAAM,EACN,SAAS,GAKV,EAAE,EAAE;IACH,OAAO,CACL,uCAAK,SAAS,EAAE,oBAAE,CAAC,6BAAC,CAAC,QAAQ,EAAE,SAAS,CAAC,EAAE,OAAO,EAAC,aAAa;QAC9D,0CACE,EAAE,EAAC,IAAI,EACP,EAAE,EAAC,IAAI,EACP,CAAC,EAAC,IAAI,EACN,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,SAAS,EAChB,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,KAAK,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,GACnC;QACV,0CACE,EAAE,EAAC,IAAI,EACP,EAAE,EAAC,IAAI,EACP,CAAC,EAAC,IAAI,EACN,IAAI,EAAC,MAAM,EACX,MAAM,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EACnD,WAAW,EAAC,GAAG,EACf,aAAa,EAAC,OAAO,EACrB,eAAe,EAAC,KAAK,EACrB,gBAAgB,EAAE,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,UAAU,CAAC,GAAG,GAAG,EACxD,KAAK,EAAE,EAAE,SAAS,EAAE,qCAAqC,EAAE,GACnD;QACV,wCAAM,UAAU,EAAC,QAAQ,EAAC,CAAC,EAAC,IAAI,EAAC,CAAC,EAAC,IAAI;YACpC,UAAU;gBACN,CACH,CACP,CAAA;AACH,CAAC,CAAA;AAtCY,QAAA,QAAQ,YAsCpB"}
@@ -0,0 +1,7 @@
/// <reference types="react" />
import { AppJob } from '../../../../@types/app';
import { Status } from '../../constants';
export declare const Timeline: ({ job, status, }: {
job: AppJob;
status: Status;
}) => JSX.Element;
@@ -0,0 +1,45 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Timeline = void 0;
const date_fns_1 = require("date-fns");
const react_1 = __importDefault(require("react"));
const Timeline_module_css_1 = __importDefault(require("./Timeline.module.css"));
const formatDate = (ts) => {
if (date_fns_1.isToday(ts)) {
return date_fns_1.format(ts, 'HH:mm:ss');
}
return date_fns_1.getYear(ts) === date_fns_1.getYear(new Date())
? date_fns_1.format(ts, 'MM/dd HH:mm:ss')
: date_fns_1.format(ts, 'MM/dd/yyyy HH:mm:ss');
};
const Timeline = function Timeline({ job, status, }) {
return (react_1.default.createElement("div", { className: Timeline_module_css_1.default.timelineWrapper },
react_1.default.createElement("ul", { className: Timeline_module_css_1.default.timeline },
react_1.default.createElement("li", null,
react_1.default.createElement("small", null, "Added at"),
react_1.default.createElement("time", null, formatDate(job.timestamp || 0))),
!!job.delay && job.delay > 0 && status === 'delayed' && (react_1.default.createElement("li", null,
react_1.default.createElement("small", null, "Delayed for"),
react_1.default.createElement("time", null, date_fns_1.formatDistance(job.timestamp || 0, (job.timestamp || 0) + job.delay, { includeSeconds: true })))),
!!job.processedOn && (react_1.default.createElement("li", null,
react_1.default.createElement("small", null,
job.delay && job.delay > 0 ? 'delayed for ' : '',
date_fns_1.formatDistance(job.processedOn, job.timestamp || 0, {
includeSeconds: true,
})),
react_1.default.createElement("small", null, "Process started at"),
react_1.default.createElement("time", null, formatDate(job.processedOn)))),
!!job.finishedOn && (react_1.default.createElement("li", null,
react_1.default.createElement("small", null, date_fns_1.formatDistance(job.finishedOn, job.processedOn || 0, {
includeSeconds: true,
})),
react_1.default.createElement("small", null,
status === 'failed' ? 'Failed' : 'Finished',
" at"),
react_1.default.createElement("time", null, formatDate(job.finishedOn)))))));
};
exports.Timeline = Timeline;
//# sourceMappingURL=Timeline.js.map
@@ -0,0 +1 @@
{"version":3,"file":"Timeline.js","sourceRoot":"","sources":["../../../../../src/ui/components/JobCard/Timeline/Timeline.tsx"],"names":[],"mappings":";;;;;;AAAA,uCAAmE;AACnE,kDAAyB;AAGzB,gFAAqC;AAIrC,MAAM,UAAU,GAAG,CAAC,EAAa,EAAE,EAAE;IACnC,IAAI,kBAAO,CAAC,EAAE,CAAC,EAAE;QACf,OAAO,iBAAM,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;KAC9B;IAED,OAAO,kBAAO,CAAC,EAAE,CAAC,KAAK,kBAAO,CAAC,IAAI,IAAI,EAAE,CAAC;QACxC,CAAC,CAAC,iBAAM,CAAC,EAAE,EAAE,gBAAgB,CAAC;QAC9B,CAAC,CAAC,iBAAM,CAAC,EAAE,EAAE,qBAAqB,CAAC,CAAA;AACvC,CAAC,CAAA;AAEM,MAAM,QAAQ,GAAG,SAAS,QAAQ,CAAC,EACxC,GAAG,EACH,MAAM,GAIP;IACC,OAAO,CACL,uCAAK,SAAS,EAAE,6BAAC,CAAC,eAAe;QAC/B,sCAAI,SAAS,EAAE,6BAAC,CAAC,QAAQ;YACvB;gBACE,wDAAuB;gBACvB,4CAAO,UAAU,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,CAAQ,CAC1C;YACJ,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,MAAM,KAAK,SAAS,IAAI,CACvD;gBACE,2DAA0B;gBAC1B,4CACG,yBAAc,CACb,GAAG,CAAC,SAAS,IAAI,CAAC,EAClB,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,EAChC,EAAE,cAAc,EAAE,IAAI,EAAE,CACzB,CACI,CACJ,CACN;YACA,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,CACpB;gBACE;oBACG,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE;oBAChD,yBAAc,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,SAAS,IAAI,CAAC,EAAE;wBACnD,cAAc,EAAE,IAAI;qBACrB,CAAC,CACI;gBACR,kEAAiC;gBACjC,4CAAO,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAQ,CACvC,CACN;YACA,CAAC,CAAC,GAAG,CAAC,UAAU,IAAI,CACnB;gBACE,6CACG,yBAAc,CAAC,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,WAAW,IAAI,CAAC,EAAE;oBACpD,cAAc,EAAE,IAAI;iBACrB,CAAC,CACI;gBACR;oBAAQ,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;0BAAY;gBAC/D,4CAAO,UAAU,CAAC,GAAG,CAAC,UAAU,CAAC,CAAQ,CACtC,CACN,CACE,CACD,CACP,CAAA;AACH,CAAC,CAAA;AApDY,QAAA,QAAQ,YAoDpB"}