missing stuff from saas specific code
This commit is contained in:
parent
55bd28e383
commit
3bf34cc652
22 changed files with 374 additions and 27 deletions
|
|
@ -10,6 +10,7 @@ import PublicRoutes from 'App/PublicRoutes';
|
|||
import { useStore } from 'App/mstore';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import * as routes from './routes';
|
||||
import Tracker from 'App/Tracker';
|
||||
|
||||
const components: any = {
|
||||
SessionPure: lazy(() => import('Components/Session/Session')),
|
||||
|
|
@ -43,6 +44,7 @@ function IFrameRoutes(props: Props) {
|
|||
<ModalProvider>
|
||||
<Layout hideHeader>
|
||||
<Loader loading={!!loading} className="flex-1">
|
||||
<Tracker />
|
||||
<Suspense fallback={<Loader loading className="flex-1" />}>
|
||||
<Switch key="content">
|
||||
<Route
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import { ModalProvider as NewModalProvider } from 'Components/ModalContext';
|
|||
import { Loader } from 'UI';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import * as routes from './routes';
|
||||
import Tracker from 'App/Tracker';
|
||||
|
||||
interface RouterProps extends RouteComponentProps {
|
||||
match: {
|
||||
|
|
@ -213,6 +214,7 @@ const Router: React.FC<RouterProps> = (props) => {
|
|||
<NewModalProvider>
|
||||
<ModalProvider>
|
||||
<Loader loading={loading} className="flex-1">
|
||||
<Tracker />
|
||||
<Layout hideHeader={hideHeader}>
|
||||
<PrivateRoutes />
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ function Modules() {
|
|||
const { userStore } = useStore();
|
||||
const { updateModule } = userStore;
|
||||
const modules = userStore.account.settings?.modules ?? [];
|
||||
const isEnterprise = userStore.account.edition === 'ee';
|
||||
const isEnterprise = userStore.isEnterprise;
|
||||
const [modulesState, setModulesState] = React.useState<any[]>([]);
|
||||
|
||||
const onToggle = async (module: any) => {
|
||||
|
|
|
|||
15
frontend/app/components/Client/Modules/extra.ts
Normal file
15
frontend/app/components/Client/Modules/extra.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
const extra = (t: any) => []
|
||||
|
||||
export const enum MODULES {
|
||||
ASSIST = 'assist',
|
||||
HIGHLIGHTS = 'notes',
|
||||
BUG_REPORTS = 'bug-reports',
|
||||
OFFLINE_RECORDINGS = 'offline-recordings',
|
||||
ALERTS = 'alerts',
|
||||
ASSIST_STATS = 'assist-stats',
|
||||
FEATURE_FLAGS = 'feature-flags',
|
||||
RECOMMENDATIONS = 'recommendations',
|
||||
USABILITY_TESTS = 'usability-tests',
|
||||
}
|
||||
|
||||
export default extra;
|
||||
|
|
@ -1,18 +1,9 @@
|
|||
import { TFunction } from 'i18next';
|
||||
import extraModules, { MODULES } from './extra';
|
||||
export * from './extra';
|
||||
|
||||
export { default } from './Modules';
|
||||
|
||||
export const enum MODULES {
|
||||
ASSIST = 'assist',
|
||||
HIGHLIGHTS = 'notes',
|
||||
BUG_REPORTS = 'bug-reports',
|
||||
OFFLINE_RECORDINGS = 'offline-recordings',
|
||||
ALERTS = 'alerts',
|
||||
ASSIST_STATS = 'assist-stats',
|
||||
FEATURE_FLAGS = 'feature-flags',
|
||||
RECOMMENDATIONS = 'recommendations',
|
||||
USABILITY_TESTS = 'usability-tests',
|
||||
}
|
||||
|
||||
export interface Module {
|
||||
label: string;
|
||||
|
|
@ -73,15 +64,6 @@ export const modules = (t: TFunction) => [
|
|||
key: MODULES.FEATURE_FLAGS,
|
||||
icon: 'toggles',
|
||||
},
|
||||
{
|
||||
label: t('Recommendations'),
|
||||
description: t(
|
||||
'Get personalized recommendations for sessions to watch, based on your replay history and search preferences.',
|
||||
),
|
||||
key: MODULES.RECOMMENDATIONS,
|
||||
icon: 'magic',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
label: t('Usability Tests'),
|
||||
description: t(
|
||||
|
|
@ -90,4 +72,5 @@ export const modules = (t: TFunction) => [
|
|||
key: MODULES.USABILITY_TESTS,
|
||||
icon: 'clipboard-check',
|
||||
},
|
||||
...extraModules(t),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ const DATE_RANGE_LABELS = {
|
|||
[CUSTOM_RANGE]: 'Custom Range',
|
||||
};
|
||||
|
||||
const LONG_RANGE_LABELS = {
|
||||
LAST_3_MONTHS: 'Last 3 Months',
|
||||
LAST_YEAR: 'Last Year',
|
||||
CUSTOM_RANGE: 'Custom Range',
|
||||
}
|
||||
|
||||
const COMPARISON_DATE_RANGE_LABELS = {
|
||||
PREV_24_HOURS: 'Previous Day',
|
||||
PREV_7_DAYS: 'Previous Week',
|
||||
|
|
@ -26,18 +32,28 @@ const COMPARISON_DATE_RANGE_LABELS = {
|
|||
};
|
||||
|
||||
const DATE_RANGE_VALUES = {};
|
||||
const LONG_RANGE_VALUES = {};
|
||||
Object.keys(DATE_RANGE_LABELS).forEach((key) => {
|
||||
DATE_RANGE_VALUES[key] = key;
|
||||
});
|
||||
Object.keys(LONG_RANGE_LABELS).forEach((key) => {
|
||||
LONG_RANGE_VALUES[key] = key;
|
||||
});
|
||||
|
||||
export { DATE_RANGE_VALUES };
|
||||
export { DATE_RANGE_VALUES, LONG_RANGE_LABELS };
|
||||
export const dateRangeValues = Object.keys(DATE_RANGE_VALUES);
|
||||
|
||||
export const DATE_RANGE_OPTIONS =
|
||||
export const DATE_RANGE_OPTIONS =
|
||||
Object.keys(DATE_RANGE_LABELS).map((key) => ({
|
||||
label: DATE_RANGE_LABELS[key],
|
||||
value: key,
|
||||
}));
|
||||
export const LONG_DATE_RANGE_OPTIONS =
|
||||
Object.keys(LONG_RANGE_LABELS).map((key) => ({
|
||||
label: LONG_RANGE_LABELS[key],
|
||||
value: key,
|
||||
}));
|
||||
|
||||
export const DATE_RANGE_COMPARISON_OPTIONS =
|
||||
Object.keys(COMPARISON_DATE_RANGE_LABELS).map((key) => ({
|
||||
label: COMPARISON_DATE_RANGE_LABELS[key],
|
||||
|
|
@ -45,9 +61,11 @@ export const DATE_RANGE_COMPARISON_OPTIONS =
|
|||
}));
|
||||
|
||||
export function getDateRangeLabel(value, t) {
|
||||
return t(DATE_RANGE_LABELS[value]);
|
||||
const string = DATE_RANGE_LABELS[value] ?? LONG_RANGE_LABELS[value];
|
||||
return t(string);
|
||||
}
|
||||
|
||||
|
||||
export function getDateRangeFromValue(value) {
|
||||
const tz = JSON.parse(localStorage.getItem(TIMEZONE));
|
||||
const offset = tz ? tz.value : undefined;
|
||||
|
|
@ -106,6 +124,14 @@ export function getDateRangeFromValue(value) {
|
|||
// return Interval.fromDateTimes(now.startOf('year'), now.endOf('year'));
|
||||
// case DATE_RANGE_VALUES.CUSTOM_RANGE:
|
||||
// return Interval.fromDateTimes(now, now);
|
||||
case LONG_RANGE_VALUES.LAST_YEAR:
|
||||
const lastYear = now.minus({ years: 1 });
|
||||
return Interval.fromDateTimes(lastYear.startOf('year'), lastYear.endOf('year'));
|
||||
case LONG_RANGE_VALUES.LAST_3_MONTHS:
|
||||
return Interval.fromDateTimes(
|
||||
now.minus({ months: 3 }).startOf('month'),
|
||||
now.endOf('month'),
|
||||
);
|
||||
default:
|
||||
throw new Error('Invalid date range value');
|
||||
}
|
||||
|
|
|
|||
2
frontend/app/extraRoutes.ts
Normal file
2
frontend/app/extraRoutes.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export const routeIdRequired = [];
|
||||
export const changeAvailable = [];
|
||||
2
frontend/app/mstore/billingStore.ts
Normal file
2
frontend/app/mstore/billingStore.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export default class BillingStore {}
|
||||
// SAAS only
|
||||
2
frontend/app/mstore/clipStore.ts
Normal file
2
frontend/app/mstore/clipStore.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export default class ClipStore {}
|
||||
// SAAS only
|
||||
|
|
@ -34,6 +34,8 @@ import userStore from './userStore';
|
|||
import UxtestingStore from './uxtestingStore';
|
||||
import WeeklyReportStore from './weeklyReportConfigStore';
|
||||
import logger from '@/logger';
|
||||
import BillingStore from "@/mstore/billingStore";
|
||||
import ClipStore from "@/mstore/clipStore";
|
||||
|
||||
const projectStore = new ProjectsStore();
|
||||
const sessionStore = new SessionStore();
|
||||
|
|
@ -110,6 +112,8 @@ export class RootStore {
|
|||
searchStoreLive: SearchStoreLive;
|
||||
integrationsStore: IntegrationsStore;
|
||||
projectsStore: ProjectsStore;
|
||||
billingStore: BillingStore;
|
||||
clipStore: ClipStore;
|
||||
|
||||
constructor() {
|
||||
this.dashboardStore = new DashboardStore();
|
||||
|
|
@ -142,6 +146,8 @@ export class RootStore {
|
|||
this.searchStore = searchStore;
|
||||
this.searchStoreLive = searchStoreLive;
|
||||
this.integrationsStore = new IntegrationsStore();
|
||||
this.billingStore = new BillingStore();
|
||||
this.clipStore = new ClipStore();
|
||||
}
|
||||
|
||||
initClient() {
|
||||
|
|
|
|||
|
|
@ -94,7 +94,8 @@ class UserStore {
|
|||
get isEnterprise() {
|
||||
return (
|
||||
this.account?.edition === 'ee' ||
|
||||
this.authStore.authDetails?.edition === 'ee'
|
||||
this.authStore.authDetails?.edition === 'ee' ||
|
||||
this.account?.plan?.type === 'enterprise'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ export default class Player extends Animator {
|
|||
}
|
||||
|
||||
// toggle range (start, end)
|
||||
toggleRange(start: number, end: number) {
|
||||
toggleRange = (start: number, end: number) => {
|
||||
this.pState.update({ range: [start, end] });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { CLIENT_TABS } from './utils/routeUtils'
|
||||
import { routeIdRequired, changeAvailable } from './extraRoutes';
|
||||
|
||||
export * from './extraRoutes';
|
||||
|
||||
const hashed = (path: string, hash?: string | number): string => {
|
||||
if ((typeof hash === 'string' && hash !== '') || typeof hash === 'number') {
|
||||
|
|
@ -191,6 +194,7 @@ export const highlights = (): string => '/highlights';
|
|||
export const kai = (): string => '/kai';
|
||||
|
||||
const REQUIRED_SITE_ID_ROUTES = [
|
||||
...routeIdRequired,
|
||||
liveSession(''),
|
||||
session(''),
|
||||
sessions(),
|
||||
|
|
@ -263,6 +267,7 @@ export function isRoute(route: string, path: string): boolean {
|
|||
}
|
||||
|
||||
const SITE_CHANGE_AVAILABLE_ROUTES = [
|
||||
...changeAvailable,
|
||||
sessions(),
|
||||
notes(),
|
||||
bookmarks(),
|
||||
|
|
|
|||
|
|
@ -25,4 +25,10 @@ export default class ConfigService extends BaseService {
|
|||
.then((r) => r.json())
|
||||
.then((j) => j.data);
|
||||
}
|
||||
|
||||
async checkElasticConnection(params: any): Promise<any> {
|
||||
return this.client.post('/integartions/elasticsearch/test', params)
|
||||
.then((r) => r.json())
|
||||
.then((j) => j.data)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ export default class SettingsService {
|
|||
.catch((e) => Promise.reject(e));
|
||||
}
|
||||
|
||||
getFirstMobUrl(sessionId: string): Promise<{ domURL: string[], fileKey?: string }> {
|
||||
getFirstMobUrl(
|
||||
sessionId: string,
|
||||
): Promise<{ domURL: string[]; fileKey?: string }> {
|
||||
return this.client
|
||||
.get(`/sessions/${sessionId}/first-mob`)
|
||||
.then((r) => r.json())
|
||||
|
|
@ -53,6 +55,44 @@ export default class SettingsService {
|
|||
.catch(console.error);
|
||||
}
|
||||
|
||||
getRecommendedSessions(sort?: any): Promise<{
|
||||
sessions: ISession[];
|
||||
total: number;
|
||||
}> {
|
||||
return this.client
|
||||
.post('/sessions-recommendations', sort)
|
||||
.then((r) => r.json())
|
||||
.then((response) => response || [])
|
||||
.catch((e) => Promise.reject(e));
|
||||
}
|
||||
|
||||
getFinetuneSessions(): Promise<{ sessions: string[] }> {
|
||||
return this.client
|
||||
.get('/PROJECT_ID/finetuning/sessions')
|
||||
.then((r) => r.json())
|
||||
.catch(Promise.reject);
|
||||
}
|
||||
|
||||
sendFeedback(data: any): Promise<any> {
|
||||
return this.client
|
||||
.post(`/session-feedback`, data)
|
||||
.then((r) => r.json())
|
||||
.then((j) => j.data || [])
|
||||
.catch(Promise.reject);
|
||||
}
|
||||
|
||||
signalFinetune() {
|
||||
return this.client.get('/PROJECT_ID/finetune');
|
||||
}
|
||||
|
||||
checkFeedback(sessionId: string): Promise<any> {
|
||||
return this.client
|
||||
.get(`/session-feedback/${sessionId}`)
|
||||
.then((r) => r.json())
|
||||
.then((j) => j.data || false)
|
||||
.catch(Promise.reject);
|
||||
}
|
||||
|
||||
getSessionInfo(sessionId: string, isLive?: boolean): Promise<ISession> {
|
||||
return this.client
|
||||
.get(
|
||||
|
|
@ -129,6 +169,22 @@ export default class SettingsService {
|
|||
.catch(Promise.reject);
|
||||
}
|
||||
|
||||
async fetchSimilarSessions(
|
||||
sessionId: string,
|
||||
params: any,
|
||||
): Promise<{ sessions: ISession[] }> {
|
||||
try {
|
||||
const r = await this.client.post(
|
||||
`/PROJECT_ID/similar-sessions/${sessionId}`,
|
||||
params,
|
||||
);
|
||||
const j = await r.json();
|
||||
return j.sessions || [];
|
||||
} catch (reason) {
|
||||
return Promise.reject(reason);
|
||||
}
|
||||
}
|
||||
|
||||
async getAssistCredentials(): Promise<any> {
|
||||
try {
|
||||
const r = await this.client.get('/config/assist/credentials');
|
||||
|
|
@ -138,4 +194,26 @@ export default class SettingsService {
|
|||
return Promise.reject(reason);
|
||||
}
|
||||
}
|
||||
|
||||
generateShorts(projectId: string) {
|
||||
try {
|
||||
void this.client.post(`/${projectId}/generate/shorts`, {});
|
||||
} catch (reason) {
|
||||
console.error('Error generating shorts:', reason);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchSessionClips(): Promise<{ clips: any[] }> {
|
||||
try {
|
||||
const r = await this.client.get('/PROJECT_ID/shorts-recommendations', {
|
||||
sortBy: 'startTs',
|
||||
sortOrder: 'desc',
|
||||
});
|
||||
// .get('/PROJECT_ID/shorts-recommendations');
|
||||
const j = await r.json();
|
||||
return j || {};
|
||||
} catch (reason) {
|
||||
return Promise.reject(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
frontend/app/svg/icons/arrow-pointer.svg
Normal file
3
frontend/app/svg/icons/arrow-pointer.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0.11 0.18 40.55 40.27">
|
||||
<path d="M1.11279 0.182831C0.560508 0.182831 0.112793 0.630546 0.112793 1.18283C0.112793 1.73512 0.560508 2.18283 1.11279 2.18283L1.11279 0.182831ZM32.5924 40.1554C32.9829 40.5459 33.6161 40.5459 34.0066 40.1554L40.3706 33.7914C40.7611 33.4009 40.7611 32.7677 40.3706 32.3772C39.98 31.9867 39.3469 31.9867 38.9564 32.3772L33.2995 38.0341L27.6426 32.3772C27.2521 31.9867 26.619 31.9867 26.2284 32.3772C25.8379 32.7677 25.8379 33.4009 26.2284 33.7914L32.5924 40.1554ZM1.11279 2.18283C9.04936 2.18283 14.9162 3.5514 19.2628 5.7905C23.5955 8.02247 26.4705 11.1461 28.3935 14.7581C32.2781 22.0548 32.2995 31.3525 32.2995 39.4483H34.2995C34.2995 31.4709 34.3209 21.6359 30.1589 13.8182C28.0584 9.87288 24.8985 6.44392 20.1787 4.01254C15.4727 1.5883 9.26958 0.182831 1.11279 0.182831L1.11279 2.18283Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 906 B |
54
frontend/app/svg/icons/clips-icon.svg
Normal file
54
frontend/app/svg/icons/clips-icon.svg
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<svg viewBox="0 0 46 46" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_66_7413)">
|
||||
<rect width="24" height="30" transform="translate(13 16)" fill="white" fill-opacity="0.01"/>
|
||||
<path d="M37.3252 19.0175H12.6756C11.8553 19.0175 11.343 19.9114 11.7549 20.6246L19.5428 33.8634V41.9192C19.5428 42.5119 20.0183 42.9907 20.6075 42.9907H29.3933C29.9825 42.9907 30.458 42.5119 30.458 41.9192V33.8634L38.2493 20.6246C38.6578 19.9114 38.1455 19.0175 37.3252 19.0175ZM28.0607 40.58H21.9401V35.3567H28.064V40.58H28.0607ZM28.3821 32.6581L28.064 33.2139H21.9368L21.6187 32.6581L14.9792 21.4282H35.0216L28.3821 32.6581Z" fill="url(#paint0_linear_66_7413)" fill-opacity="0.85"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip1_66_7413)">
|
||||
<rect width="16" height="16" transform="translate(0.957764 3.83905)" fill="white" fill-opacity="0.01"/>
|
||||
<path d="M3.79422 12.8529L4.90769 12.3336L5.4269 13.4471C5.44049 13.4762 5.48019 13.4853 5.51579 13.4687L6.29263 13.1065C6.32823 13.0899 6.3468 13.0536 6.33246 13.0229L5.814 11.911L6.92746 11.3918C6.95659 11.3782 6.96566 11.3385 6.94906 11.3029L6.58682 10.5261C6.57021 10.4905 6.53397 10.4719 6.50322 10.4862L5.39138 11.0047L4.87216 9.89125C4.85858 9.86212 4.81888 9.85304 4.78327 9.86965L4.00644 10.2319C3.97083 10.2485 3.95227 10.2847 3.96585 10.3139L4.48507 11.4273L3.37161 11.9465C3.34247 11.9601 3.3334 11.9998 3.35 12.0354L3.71225 12.8123C3.72885 12.8479 3.76509 12.8664 3.79422 12.8529ZM9.1026 10.3775L12.2358 8.91647C12.265 8.90288 12.274 8.86319 12.2574 8.82758L11.8952 8.05075C11.8786 8.01514 11.8423 7.99658 11.8116 8.01091L8.67998 9.47121C8.65085 9.4848 8.64177 9.52449 8.65838 9.5601L9.02062 10.3369C9.03722 10.3725 9.07347 10.3911 9.1026 10.3775ZM11.1025 14.6663L14.2357 13.2052C14.2649 13.1917 14.2739 13.152 14.2573 13.1164L13.8951 12.3395C13.8785 12.3039 13.8422 12.2854 13.8115 12.2997L10.6799 13.76C10.6507 13.7736 10.6417 13.8133 10.6583 13.8489L11.0205 14.6257C11.0371 14.6613 11.0734 14.6799 11.1025 14.6663ZM11.8874 16.3494L15.0206 14.8884C15.0497 14.8748 15.0588 14.8351 15.0422 14.7995L14.6799 14.0227C14.6633 13.9871 14.6271 13.9685 14.5964 13.9828L11.4647 15.4431C11.4356 15.4567 11.4265 15.4964 11.4431 15.532L11.8054 16.3089C11.822 16.3445 11.8582 16.363 11.8874 16.3494ZM8.10884 16.5154L8.53405 14.8414C8.5644 14.7248 8.44652 14.6241 8.33971 14.6739L7.51755 15.0573C7.48033 15.0746 7.45227 15.1074 7.44396 15.1487L7.20764 16.0786L6.34344 15.6619C6.32521 15.653 6.30528 15.6482 6.28502 15.6478C6.26475 15.6474 6.24464 15.6514 6.22607 15.6595L5.4023 16.0436C5.29548 16.0934 5.29684 16.2485 5.40567 16.3002L6.96139 17.0505L6.52624 18.7666C6.49427 18.884 6.61216 18.9847 6.71897 18.9349L7.54112 18.5515C7.57835 18.5341 7.6064 18.5013 7.61472 18.46L7.86097 17.4881L8.76379 17.9242C8.80165 17.942 8.84556 17.9432 8.88116 17.9266L9.70331 17.5432C9.81013 17.4934 9.80877 17.3384 9.69994 17.2867L8.10884 16.5154Z" fill="url(#paint1_linear_66_7413)" fill-opacity="0.85"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip2_66_7413)">
|
||||
<rect width="15" height="15" transform="translate(16.9578 0.611725)" fill="white" fill-opacity="0.01"/>
|
||||
<path d="M19.5945 4.88907H20.7664V11.3344H19.5945V4.88907ZM24.9998 7.52579H23.9158C23.9041 7.52579 23.8929 7.53042 23.8847 7.53866C23.8765 7.5469 23.8718 7.55808 23.8718 7.56973V8.65372C23.8718 8.66537 23.8765 8.67655 23.8847 8.68479C23.8929 8.69303 23.9041 8.69766 23.9158 8.69766H24.9998C25.0114 8.69766 25.0226 8.69303 25.0308 8.68479C25.0391 8.67655 25.0437 8.66537 25.0437 8.65372V7.56973C25.0437 7.55808 25.0391 7.5469 25.0308 7.53866C25.0226 7.53042 25.0114 7.52579 24.9998 7.52579V7.52579ZM28.1492 5.94376H29.321V10.2797H28.1492V5.94376Z" fill="#E2E4F6"/>
|
||||
<path d="M30.2 4.94766H29.2332V3.54141C29.2332 3.47696 29.1804 3.42422 29.116 3.42422H28.3542C28.2898 3.42422 28.2371 3.47696 28.2371 3.54141V4.94766H27.2703C27.2058 4.94766 27.1531 5.0004 27.1531 5.06485V11.1586C27.1531 11.2231 27.2058 11.2758 27.2703 11.2758H28.2371V12.682C28.2371 12.7465 28.2898 12.7992 28.3542 12.7992H29.116C29.1804 12.7992 29.2332 12.7465 29.2332 12.682V11.2758H30.2C30.2644 11.2758 30.3171 11.2231 30.3171 11.1586V5.06485C30.3171 5.0004 30.2644 4.94766 30.2 4.94766ZM29.321 10.2797H28.1492V5.94376H29.321V10.2797ZM25.9226 6.52969H24.9558V4.01016C24.9558 3.94571 24.9031 3.89297 24.8386 3.89297H24.0769C24.0125 3.89297 23.9597 3.94571 23.9597 4.01016V6.52969H22.9929C22.9285 6.52969 22.8757 6.58243 22.8757 6.64688V9.57657C22.8757 9.64102 22.9285 9.69376 22.9929 9.69376H23.9597V12.2133C23.9597 12.2777 24.0125 12.3305 24.0769 12.3305H24.8386C24.9031 12.3305 24.9558 12.2777 24.9558 12.2133V9.69376H25.9226C25.9871 9.69376 26.0398 9.64102 26.0398 9.57657V6.64688C26.0398 6.58243 25.9871 6.52969 25.9226 6.52969ZM25.0437 8.65372C25.0437 8.66537 25.0391 8.67655 25.0308 8.68479C25.0226 8.69303 25.0114 8.69766 24.9998 8.69766H23.9158C23.9041 8.69766 23.8929 8.69303 23.8847 8.68479C23.8765 8.67655 23.8718 8.66537 23.8718 8.65372V7.56973C23.8718 7.55808 23.8765 7.5469 23.8847 7.53866C23.8929 7.53042 23.9041 7.52579 23.9158 7.52579H24.9998C25.0114 7.52579 25.0226 7.53042 25.0308 7.53866C25.0391 7.5469 25.0437 7.55808 25.0437 7.56973V8.65372ZM21.6453 3.89297H20.6785V3.07266C20.6785 3.00821 20.6257 2.95547 20.5613 2.95547H19.7996C19.7351 2.95547 19.6824 3.00821 19.6824 3.07266V3.89297H18.7156C18.6511 3.89297 18.5984 3.94571 18.5984 4.01016V12.2133C18.5984 12.2777 18.6511 12.3305 18.7156 12.3305H19.6824V13.1508C19.6824 13.2152 19.7351 13.268 19.7996 13.268H20.5613C20.6257 13.268 20.6785 13.2152 20.6785 13.1508V12.3305H21.6453C21.7097 12.3305 21.7625 12.2777 21.7625 12.2133V4.01016C21.7625 3.94571 21.7097 3.89297 21.6453 3.89297ZM20.7664 11.3344H19.5945V4.88907H20.7664V11.3344Z" fill="url(#paint2_linear_66_7413)" fill-opacity="0.85"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip3_66_7413)">
|
||||
<rect width="14.6667" height="16" transform="matrix(0 1 -1 0 45.321 5.93465)" fill="white" fill-opacity="0.01"/>
|
||||
<g clip-path="url(#clip4_66_7413)">
|
||||
<path d="M36.0824 6.26764C35.7036 6.26764 35.3402 6.41815 35.0723 6.68606C34.8044 6.95397 34.6539 7.31733 34.6539 7.69621C34.6539 8.07509 34.8044 8.43845 35.0723 8.70636C35.3402 8.97427 35.7036 9.12478 36.0824 9.12478C36.4613 9.12478 36.8247 8.97427 37.0926 8.70636C37.3605 8.43845 37.511 8.07509 37.511 7.69621C37.511 7.31733 37.3605 6.95397 37.0926 6.68606C36.8247 6.41815 36.4613 6.26764 36.0824 6.26764ZM41.0824 8.90157C40.7983 8.90157 40.5257 9.01445 40.3248 9.21538C40.1239 9.41631 40.011 9.68884 40.011 9.973C40.011 10.2572 40.1239 10.5297 40.3248 10.7306C40.5257 10.9315 40.7983 11.0444 41.0824 11.0444C41.3666 11.0444 41.6391 10.9315 41.84 10.7306C42.041 10.5297 42.1539 10.2572 42.1539 9.973C42.1539 9.68884 42.041 9.41631 41.84 9.21538C41.6391 9.01445 41.3666 8.90157 41.0824 8.90157ZM35.9931 12.4284C35.4248 12.4284 34.8798 12.6541 34.4779 13.056C34.0761 13.4578 33.8503 14.0029 33.8503 14.5712C33.8503 15.1395 34.0761 15.6846 34.4779 16.0864C34.8798 16.4883 35.4248 16.7141 35.9931 16.7141C36.5615 16.7141 37.1065 16.4883 37.5084 16.0864C37.9102 15.6846 38.136 15.1395 38.136 14.5712C38.136 14.0029 37.9102 13.4578 37.5084 13.056C37.1065 12.6541 36.5615 12.4284 35.9931 12.4284ZM43.0021 15.7319C42.6706 15.7319 42.3526 15.8636 42.1182 16.098C41.8838 16.3325 41.7521 16.6504 41.7521 16.9819C41.7521 17.3134 41.8838 17.6314 42.1182 17.8658C42.3526 18.1002 42.6706 18.2319 43.0021 18.2319C43.3336 18.2319 43.6515 18.1002 43.886 17.8658C44.1204 17.6314 44.2521 17.3134 44.2521 16.9819C44.2521 16.6504 44.1204 16.3325 43.886 16.098C43.6515 15.8636 43.3336 15.7319 43.0021 15.7319Z" fill="url(#paint3_linear_66_7413)" fill-opacity="0.85"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_66_7413" x1="25.0011" y1="19.0175" x2="25.0011" y2="42.9907" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#3ECEAC"/>
|
||||
<stop offset="1" stop-color="#050EFF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_66_7413" x1="5.93962" y1="6.43406" x2="11.977" y2="19.3813" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#050EFF"/>
|
||||
<stop offset="1" stop-color="#5AD4B9"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_66_7413" x1="24.4578" y1="2.95547" x2="24.4578" y2="13.268" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#050EFF"/>
|
||||
<stop offset="1" stop-color="#5AD3BA"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_66_7413" x1="45.9932" y1="11.2676" x2="30.2789" y2="11.2676" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#050EFF"/>
|
||||
<stop offset="1" stop-color="#5AD4B9"/>
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_66_7413">
|
||||
<rect width="24" height="30" fill="white" transform="translate(13 16)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_66_7413">
|
||||
<rect width="16" height="16" fill="white" transform="translate(0.957764 3.83905)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip2_66_7413">
|
||||
<rect width="15" height="15" fill="white" transform="translate(16.9578 0.611725)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip3_66_7413">
|
||||
<rect width="14.6667" height="16" fill="white" transform="matrix(0 1 -1 0 45.321 5.93465)"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip4_66_7413">
|
||||
<rect width="14.6667" height="16" fill="white" transform="matrix(0 1 -1 0 45.321 5.93465)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
3
frontend/app/svg/icons/hand-thumbs-down.svg
Normal file
3
frontend/app/svg/icons/hand-thumbs-down.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-hand-thumbs-down" viewBox="0 0 16 16">
|
||||
<path d="M8.864 15.674c-.956.24-1.843-.484-1.908-1.42-.072-1.05-.23-2.015-.428-2.59-.125-.36-.479-1.012-1.04-1.638-.557-.624-1.282-1.179-2.131-1.41C2.685 8.432 2 7.85 2 7V3c0-.845.682-1.464 1.448-1.546 1.07-.113 1.564-.415 2.068-.723l.048-.029c.272-.166.578-.349.97-.484C6.931.08 7.395 0 8 0h3.5c.937 0 1.599.478 1.934 1.064.164.287.254.607.254.913 0 .152-.023.312-.077.464.201.262.38.577.488.9.11.33.172.762.004 1.15.069.13.12.268.159.403.077.27.113.567.113.856 0 .289-.036.586-.113.856-.035.12-.08.244-.138.363.394.571.418 1.2.234 1.733-.206.592-.682 1.1-1.2 1.272-.847.283-1.803.276-2.516.211a9.877 9.877 0 0 1-.443-.05 9.364 9.364 0 0 1-.062 4.51c-.138.508-.55.848-1.012.964l-.261.065zM11.5 1H8c-.51 0-.863.068-1.14.163-.281.097-.506.229-.776.393l-.04.025c-.555.338-1.198.73-2.49.868-.333.035-.554.29-.554.55V7c0 .255.226.543.62.65 1.095.3 1.977.997 2.614 1.709.635.71 1.064 1.475 1.238 1.977.243.7.407 1.768.482 2.85.025.362.36.595.667.518l.262-.065c.16-.04.258-.144.288-.255a8.34 8.34 0 0 0-.145-4.726.5.5 0 0 1 .595-.643h.003l.014.004.058.013a8.912 8.912 0 0 0 1.036.157c.663.06 1.457.054 2.11-.163.175-.059.45-.301.57-.651.107-.308.087-.67-.266-1.021L12.793 7l.353-.354c.043-.042.105-.14.154-.315.048-.167.075-.37.075-.581 0-.211-.027-.414-.075-.581-.05-.174-.111-.273-.154-.315l-.353-.354.353-.354c.047-.047.109-.176.005-.488a2.224 2.224 0 0 0-.505-.804l-.353-.354.353-.354c.006-.005.041-.05.041-.17a.866.866 0 0 0-.121-.415C12.4 1.272 12.063 1 11.5 1z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
3
frontend/app/svg/icons/hand-thumbs-up.svg
Normal file
3
frontend/app/svg/icons/hand-thumbs-up.svg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" class="bi bi-hand-thumbs-up" viewBox="0 0 16 16">
|
||||
<path d="M8.864.046C7.908-.193 7.02.53 6.956 1.466c-.072 1.051-.23 2.016-.428 2.59-.125.36-.479 1.013-1.04 1.639-.557.623-1.282 1.178-2.131 1.41C2.685 7.288 2 7.87 2 8.72v4.001c0 .845.682 1.464 1.448 1.545 1.07.114 1.564.415 2.068.723l.048.03c.272.165.578.348.97.484.397.136.861.217 1.466.217h3.5c.937 0 1.599-.477 1.934-1.064a1.86 1.86 0 0 0 .254-.912c0-.152-.023-.312-.077-.464.201-.263.38-.578.488-.901.11-.33.172-.762.004-1.149.069-.13.12-.269.159-.403.077-.27.113-.568.113-.857 0-.288-.036-.585-.113-.856a2.144 2.144 0 0 0-.138-.362 1.9 1.9 0 0 0 .234-1.734c-.206-.592-.682-1.1-1.2-1.272-.847-.282-1.803-.276-2.516-.211a9.84 9.84 0 0 0-.443.05 9.365 9.365 0 0 0-.062-4.509A1.38 1.38 0 0 0 9.125.111L8.864.046zM11.5 14.721H8c-.51 0-.863-.069-1.14-.164-.281-.097-.506-.228-.776-.393l-.04-.024c-.555-.339-1.198-.731-2.49-.868-.333-.036-.554-.29-.554-.55V8.72c0-.254.226-.543.62-.65 1.095-.3 1.977-.996 2.614-1.708.635-.71 1.064-1.475 1.238-1.978.243-.7.407-1.768.482-2.85.025-.362.36-.594.667-.518l.262.066c.16.04.258.143.288.255a8.34 8.34 0 0 1-.145 4.725.5.5 0 0 0 .595.644l.003-.001.014-.003.058-.014a8.908 8.908 0 0 1 1.036-.157c.663-.06 1.457-.054 2.11.164.175.058.45.3.57.65.107.308.087.67-.266 1.022l-.353.353.353.354c.043.043.105.141.154.315.048.167.075.37.075.581 0 .212-.027.414-.075.582-.05.174-.111.272-.154.315l-.353.353.353.354c.047.047.109.177.005.488a2.224 2.224 0 0 1-.505.805l-.353.353.353.354c.006.005.041.05.041.17a.866.866 0 0 1-.121.416c-.165.288-.503.56-1.066.56z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
1
frontend/app/svg/icons/tv-minimal-play.svg
Normal file
1
frontend/app/svg/icons/tv-minimal-play.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-tv-minimal-play"><path d="M10 7.75a.75.75 0 0 1 1.142-.638l3.664 2.249a.75.75 0 0 1 0 1.278l-3.664 2.25a.75.75 0 0 1-1.142-.64z"/><path d="M7 21h10"/><rect width="20" height="14" x="2" y="3" rx="2"/></svg>
|
||||
|
After Width: | Height: | Size: 408 B |
6
frontend/app/svg/icons/user-issues.svg
Normal file
6
frontend/app/svg/icons/user-issues.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<svg viewBox="0 0 19 19" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame">
|
||||
<path id="Vector" d="M11.792 4.75C11.792 5.74456 11.3969 6.69839 10.6936 7.40165C9.99038 8.10491 9.03655 8.5 8.04199 8.5C7.04743 8.5 6.0936 8.10491 5.39034 7.40165C4.68708 6.69839 4.29199 5.74456 4.29199 4.75C4.29199 3.75544 4.68708 2.80161 5.39034 2.09835C6.0936 1.39509 7.04743 1 8.04199 1C9.03655 1 9.99038 1.39509 10.6936 2.09835C11.3969 2.80161 11.792 3.75544 11.792 4.75ZM8.04199 7.25C8.70503 7.25 9.34092 6.98661 9.80976 6.51777C10.2786 6.04893 10.542 5.41304 10.542 4.75C10.542 4.08696 10.2786 3.45107 9.80976 2.98223C9.34092 2.51339 8.70503 2.25 8.04199 2.25C7.37895 2.25 6.74307 2.51339 6.27423 2.98223C5.80538 3.45107 5.54199 4.08696 5.54199 4.75C5.54199 5.41304 5.80538 6.04893 6.27423 6.51777C6.74307 6.98661 7.37895 7.25 8.04199 7.25ZM8.36199 16C8.21859 15.5941 8.12254 15.173 8.07574 14.745H1.79199C1.79324 14.4375 1.98449 13.5125 2.83199 12.665C3.64699 11.85 5.18074 11 8.04199 11C8.36699 11 8.67574 11.0112 8.96699 11.0312C9.24949 10.605 9.58699 10.2188 9.97199 9.88375C9.38824 9.7975 8.74699 9.75 8.04199 9.75C1.79199 9.75 0.541992 13.5 0.541992 14.75C0.541992 16 1.79199 16 1.79199 16H8.36199Z" />
|
||||
<path id="Vector_2" d="M18.042 14.125C18.042 15.2853 17.5811 16.3981 16.7606 17.2186C15.9401 18.0391 14.8273 18.5 13.667 18.5C12.5067 18.5 11.3939 18.0391 10.5734 17.2186C9.75293 16.3981 9.29199 15.2853 9.29199 14.125C9.29199 12.9647 9.75293 11.8519 10.5734 11.0314C11.3939 10.2109 12.5067 9.75 13.667 9.75C14.8273 9.75 15.9401 10.2109 16.7606 11.0314C17.5811 11.8519 18.042 12.9647 18.042 14.125ZM13.667 11.625C13.5012 11.625 13.3423 11.6908 13.225 11.8081C13.1078 11.9253 13.042 12.0842 13.042 12.25V14.125C13.042 14.2908 13.1078 14.4497 13.225 14.5669C13.3423 14.6842 13.5012 14.75 13.667 14.75C13.8328 14.75 13.9917 14.6842 14.1089 14.5669C14.2261 14.4497 14.292 14.2908 14.292 14.125V12.25C14.292 12.0842 14.2261 11.9253 14.1089 11.8081C13.9917 11.6908 13.8328 11.625 13.667 11.625ZM13.667 16.625C13.8328 16.625 13.9917 16.5592 14.1089 16.4419C14.2261 16.3247 14.292 16.1658 14.292 16C14.292 15.8342 14.2261 15.6753 14.1089 15.5581C13.9917 15.4408 13.8328 15.375 13.667 15.375C13.5012 15.375 13.3423 15.4408 13.225 15.5581C13.1078 15.6753 13.042 15.8342 13.042 16C13.042 16.1658 13.1078 16.3247 13.225 16.4419C13.3423 16.5592 13.5012 16.625 13.667 16.625Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
|
|
@ -38,6 +38,7 @@ export function debounceCall(func, wait) {
|
|||
};
|
||||
}
|
||||
|
||||
|
||||
export function randomInt(a, b) {
|
||||
const min = (b ? a : 0) - 0.5;
|
||||
const max = b || a || Number.MAX_SAFE_INTEGER;
|
||||
|
|
@ -623,6 +624,152 @@ export function exportAntCsv(tableColumns, tableData, filename = 'table.csv') {
|
|||
saveAsFile(blob, filename);
|
||||
}
|
||||
|
||||
export const numFormatter = (num: any) => {
|
||||
if (num > 999 && num < 1000000) {
|
||||
return num / 1000 + 'K';
|
||||
} else if (num >= 1000000) {
|
||||
return num / 1000000 + 'M';
|
||||
} else if (num < 900) {
|
||||
return num;
|
||||
}
|
||||
};
|
||||
|
||||
export const loadStripe = (callback: any) => {
|
||||
loadDynamicScript('stripeTag', 'https://js.stripe.com/v3/', callback);
|
||||
};
|
||||
|
||||
export const loadDynamicScript = (tag: string, path: string, callback: any) => {
|
||||
const scriptExists = document.getElementById(tag)
|
||||
if (scriptExists) return callback();
|
||||
|
||||
const scriptJs = document.createElement('script');
|
||||
scriptJs.src = path;
|
||||
scriptJs.async = true;
|
||||
scriptJs.id = tag;
|
||||
scriptJs.onload = () => {
|
||||
setTimeout(callback, 10);
|
||||
};
|
||||
document.body && document.body.appendChild(scriptJs);
|
||||
}
|
||||
|
||||
export const getDeviceType = () => {
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
const isTablet = /iPad|Android/i.test(navigator.userAgent) && !/Mobile/i.test(navigator.userAgent);
|
||||
|
||||
if (isMobile) {
|
||||
return 2;
|
||||
} else if (isTablet) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
export const sensitiveParams = new Set([
|
||||
'password',
|
||||
'pass',
|
||||
'pwd',
|
||||
'mdp',
|
||||
'token',
|
||||
'bearer',
|
||||
'jwt',
|
||||
'api_key',
|
||||
'api-key',
|
||||
'apiKey',
|
||||
'key',
|
||||
'secret',
|
||||
'id',
|
||||
'user',
|
||||
'userId',
|
||||
'email',
|
||||
'ssn',
|
||||
'name',
|
||||
'firstname',
|
||||
'lastname',
|
||||
'birthdate',
|
||||
'dob',
|
||||
'address',
|
||||
'zip',
|
||||
'zipcode',
|
||||
'x-api-key',
|
||||
'www-authenticate',
|
||||
'x-csrf-token',
|
||||
'x-requested-with',
|
||||
'x-forwarded-for',
|
||||
'x-real-ip',
|
||||
'cookie',
|
||||
'authorization',
|
||||
'auth',
|
||||
'proxy-authorization',
|
||||
'set-cookie',
|
||||
'account_key',
|
||||
])
|
||||
|
||||
export function filterHeaders(headers: Record<string, string> | { name: string; value: string }[]) {
|
||||
const filteredHeaders: Record<string, string> = {}
|
||||
if (Array.isArray(headers)) {
|
||||
headers.forEach(({ name, value }) => {
|
||||
if (sensitiveParams.has(name.toLowerCase())) {
|
||||
filteredHeaders[name] = '******'
|
||||
} else {
|
||||
filteredHeaders[name] = value
|
||||
}
|
||||
})
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(headers)) {
|
||||
if (sensitiveParams.has(key.toLowerCase())) {
|
||||
filteredHeaders[key] = '******'
|
||||
} else {
|
||||
filteredHeaders[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
return filteredHeaders
|
||||
}
|
||||
|
||||
export function filterBody<T extends Record<string, any>>(body: T): T {
|
||||
if (!body) {
|
||||
return body
|
||||
}
|
||||
|
||||
obscureSensitiveData(body)
|
||||
return body
|
||||
}
|
||||
|
||||
export function obscureSensitiveData(obj: Record<string, any> | any[]) {
|
||||
if (Array.isArray(obj)) {
|
||||
obj.forEach(obscureSensitiveData)
|
||||
} else if (obj && typeof obj === 'object') {
|
||||
for (const key in obj) {
|
||||
if (Object.hasOwn(obj, key)) {
|
||||
if (sensitiveParams.has(key.toLowerCase())) {
|
||||
obj[key] = '******'
|
||||
} else if (obj[key] !== null && typeof obj[key] === 'object') {
|
||||
obscureSensitiveData(obj[key])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function tryFilterUrl(url: string) {
|
||||
if (!url) return ''
|
||||
try {
|
||||
const urlObj = new URL(url)
|
||||
if (urlObj.searchParams) {
|
||||
for (const key of urlObj.searchParams.keys()) {
|
||||
if (sensitiveParams.has(key.toLowerCase())) {
|
||||
urlObj.searchParams.set(key, '******')
|
||||
}
|
||||
}
|
||||
}
|
||||
return urlObj.toString()
|
||||
} catch (e) {
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function roundToNextMinutes(timestamp: number, minutes: number): number {
|
||||
const date = new Date(timestamp);
|
||||
date.setSeconds(0, 0);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue