init
This commit is contained in:
41
worklenz-frontend/src/utils/calculate-time-difference.ts
Normal file
41
worklenz-frontend/src/utils/calculate-time-difference.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
differenceInSeconds,
|
||||
differenceInMinutes,
|
||||
differenceInHours,
|
||||
differenceInDays,
|
||||
differenceInWeeks,
|
||||
differenceInMonths,
|
||||
differenceInYears,
|
||||
formatDistanceToNow,
|
||||
} from 'date-fns';
|
||||
import { enUS, es, pt } from 'date-fns/locale';
|
||||
import { getLanguageFromLocalStorage } from './language-utils';
|
||||
|
||||
export function calculateTimeDifference(timestamp: string | Date): string {
|
||||
const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
|
||||
const localeString = getLanguageFromLocalStorage();
|
||||
const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt;
|
||||
const now = new Date();
|
||||
|
||||
const diffInSeconds = differenceInSeconds(now, date);
|
||||
if (diffInSeconds < 60) {
|
||||
return 'Just now';
|
||||
}
|
||||
|
||||
const distanceFunctions = [
|
||||
differenceInYears,
|
||||
differenceInMonths,
|
||||
differenceInWeeks,
|
||||
differenceInDays,
|
||||
differenceInHours,
|
||||
differenceInMinutes,
|
||||
];
|
||||
|
||||
for (const distanceFunction of distanceFunctions) {
|
||||
if (distanceFunction(now, date) > 0) {
|
||||
return formatDistanceToNow(date, { addSuffix: true, locale });
|
||||
}
|
||||
}
|
||||
|
||||
return 'Just now';
|
||||
}
|
||||
10
worklenz-frontend/src/utils/calculate-time-gap.ts
Normal file
10
worklenz-frontend/src/utils/calculate-time-gap.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import { enUS, es, pt } from "date-fns/locale";
|
||||
import { getLanguageFromLocalStorage } from "./language-utils";
|
||||
|
||||
export function calculateTimeGap(timestamp: string | Date): string {
|
||||
const localeString = getLanguageFromLocalStorage();
|
||||
const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt;
|
||||
const date = typeof timestamp === 'string' ? new Date(timestamp) : timestamp;
|
||||
return formatDistanceToNow(date, { addSuffix: true, locale });
|
||||
}
|
||||
13
worklenz-frontend/src/utils/check-task-dependency-status.ts
Normal file
13
worklenz-frontend/src/utils/check-task-dependency-status.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { tasksApiService } from "@/api/tasks/tasks.api.service";
|
||||
import logger from "./errorLogger";
|
||||
|
||||
export const checkTaskDependencyStatus = async (taskId: string, statusId: string) => {
|
||||
if (!taskId || !statusId) return false;
|
||||
try {
|
||||
const res = await tasksApiService.getTaskDependencyStatus(taskId, statusId);
|
||||
return res.done ? res.body.can_continue : false;
|
||||
} catch (error) {
|
||||
logger.error('Error checking task dependency status:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
3
worklenz-frontend/src/utils/colorUtils.ts
Normal file
3
worklenz-frontend/src/utils/colorUtils.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const tagBackground = (color: string): string => {
|
||||
return `${color}1A`; // 1A is 10% opacity in hex
|
||||
};
|
||||
12
worklenz-frontend/src/utils/current-date-string.ts
Normal file
12
worklenz-frontend/src/utils/current-date-string.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { getLanguageFromLocalStorage } from './language-utils';
|
||||
|
||||
export const currentDateString = (): string => {
|
||||
const date = dayjs();
|
||||
const localeString = getLanguageFromLocalStorage();
|
||||
const locale = localeString === 'en' ? 'en' : localeString === 'es' ? 'es' : 'pt';
|
||||
|
||||
const todayText =
|
||||
localeString === 'en' ? 'Today is' : localeString === 'es' ? 'Hoy es' : 'Hoje é';
|
||||
return `${todayText} ${date.locale(locale).format('dddd, MMMM DD, YYYY')}`;
|
||||
};
|
||||
32
worklenz-frontend/src/utils/dateUtils.ts
Normal file
32
worklenz-frontend/src/utils/dateUtils.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import dayjs from 'dayjs';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
|
||||
// Initialize the relativeTime plugin
|
||||
dayjs.extend(relativeTime);
|
||||
|
||||
/**
|
||||
* Formats a date to a relative time string (e.g., "2 hours ago", "a day ago")
|
||||
* This mimics the Angular fromNow pipe functionality
|
||||
*
|
||||
* @param date - The date to format (string, Date, or dayjs object)
|
||||
* @returns A string representing the relative time
|
||||
*/
|
||||
export const fromNow = (date: string | Date | dayjs.Dayjs): string => {
|
||||
if (!date) return '';
|
||||
return dayjs(date).fromNow();
|
||||
};
|
||||
|
||||
/**
|
||||
* Formats a date to a specific format
|
||||
*
|
||||
* @param date - The date to format (string, Date, or dayjs object)
|
||||
* @param format - The format string (default: 'YYYY-MM-DD')
|
||||
* @returns A formatted date string
|
||||
*/
|
||||
export const formatDate = (
|
||||
date: string | Date | dayjs.Dayjs,
|
||||
format: string = 'YYYY-MM-DD'
|
||||
): string => {
|
||||
if (!date) return '';
|
||||
return dayjs(date).format(format);
|
||||
};
|
||||
25
worklenz-frontend/src/utils/durationDateFormat.ts
Normal file
25
worklenz-frontend/src/utils/durationDateFormat.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
export const durationDateFormat = (date: Date | null | string | undefined): string => {
|
||||
if (!date) return '-';
|
||||
|
||||
const givenDate = new Date(date);
|
||||
const currentDate = new Date();
|
||||
|
||||
const diffInMilliseconds = currentDate.getTime() - givenDate.getTime();
|
||||
|
||||
const diffInDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24));
|
||||
const diffInMonths =
|
||||
currentDate.getMonth() -
|
||||
givenDate.getMonth() +
|
||||
12 * (currentDate.getFullYear() - givenDate.getFullYear());
|
||||
const diffInYears = currentDate.getFullYear() - givenDate.getFullYear();
|
||||
|
||||
if (diffInYears > 0) {
|
||||
return diffInYears === 1 ? '1 year ago' : `${diffInYears} years ago`;
|
||||
} else if (diffInMonths > 0) {
|
||||
return diffInMonths === 1 ? '1 month ago' : `${diffInMonths} months ago`;
|
||||
} else if (diffInDays > 0) {
|
||||
return diffInDays === 1 ? '1 day ago' : `${diffInDays} days ago`;
|
||||
} else {
|
||||
return 'Today';
|
||||
}
|
||||
};
|
||||
153
worklenz-frontend/src/utils/errorLogger.ts
Normal file
153
worklenz-frontend/src/utils/errorLogger.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
export type LogLevel = 'info' | 'success' | 'warning' | 'error' | 'debug';
|
||||
|
||||
interface LogStyles {
|
||||
title: string;
|
||||
text: string;
|
||||
background?: string;
|
||||
}
|
||||
|
||||
interface LogOptions {
|
||||
showTimestamp?: boolean;
|
||||
collapsed?: boolean;
|
||||
level?: LogLevel;
|
||||
}
|
||||
|
||||
class ConsoleLogger {
|
||||
private readonly isProduction = import.meta.env.PROD;
|
||||
|
||||
private readonly styles: Record<LogLevel, LogStyles> = {
|
||||
info: {
|
||||
title: 'color: #1890ff; font-weight: bold; font-size: 12px;',
|
||||
text: 'color: #1890ff; font-size: 12px;',
|
||||
background: 'background: transparent; padding: 2px 5px; border-radius: 2px;',
|
||||
},
|
||||
success: {
|
||||
title: 'color: #52c41a; font-weight: bold; font-size: 12px;',
|
||||
text: 'color: #52c41a; font-size: 12px;',
|
||||
background: 'background: transparent; padding: 2px 5px; border-radius: 2px;',
|
||||
},
|
||||
warning: {
|
||||
title: 'color: #faad14; font-weight: bold; font-size: 12px;',
|
||||
text: 'color: #faad14; font-size: 12px;',
|
||||
background: 'background: transparent; padding: 2px 5px; border-radius: 2px;',
|
||||
},
|
||||
error: {
|
||||
title: 'color: #ff4d4f; font-weight: bold; font-size: 12px;',
|
||||
text: 'color: #ff4d4f; font-size: 12px;',
|
||||
background: 'background: transparent; padding: 2px 5px; border-radius: 2px;',
|
||||
},
|
||||
debug: {
|
||||
title: 'color: #722ed1; font-weight: bold; font-size: 12px;',
|
||||
text: 'color: #722ed1; font-size: 12px;',
|
||||
background: 'background: transparent; padding: 2px 5px; border-radius: 2px;',
|
||||
},
|
||||
};
|
||||
|
||||
// Private helper methods
|
||||
private getTimestamp(): string {
|
||||
return new Date().toISOString();
|
||||
}
|
||||
|
||||
private formatValue(value: unknown): unknown {
|
||||
if (value instanceof Error) {
|
||||
const { name, message, stack } = value;
|
||||
return { name, message, stack };
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private formatLogMessage(title: string, showTimestamp: boolean, styles: LogStyles): string {
|
||||
const timestamp = showTimestamp ? `[${this.getTimestamp()}] ` : '';
|
||||
return `%c${timestamp}${title}`;
|
||||
}
|
||||
|
||||
private logObjectData(data: Record<string, unknown>, styles: LogStyles): void {
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
console.log(`%c${key}:`, styles.title, this.formatValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
private log(
|
||||
title: string,
|
||||
data: unknown,
|
||||
{ showTimestamp = true, collapsed = false, level = 'info' }: LogOptions = {}
|
||||
): void {
|
||||
if (this.isProduction) return;
|
||||
|
||||
const styles = this.styles[level];
|
||||
const logMethod = collapsed ? console.groupCollapsed : console.group;
|
||||
const formattedMessage = this.formatLogMessage(title, showTimestamp, styles);
|
||||
|
||||
logMethod(formattedMessage, styles.background ?? styles.title);
|
||||
|
||||
if (data !== null) {
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
this.logObjectData(data as Record<string, unknown>, styles);
|
||||
} else {
|
||||
console.log(`%cValue:`, styles.title, this.formatValue(data));
|
||||
}
|
||||
}
|
||||
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
// Public logging methods
|
||||
public info(title: string, data: unknown = null, options?: Omit<LogOptions, 'level'>): void {
|
||||
this.log(title, data, { ...options, level: 'info' });
|
||||
}
|
||||
|
||||
public success(title: string, data: unknown = null, options?: Omit<LogOptions, 'level'>): void {
|
||||
this.log(title, data, { ...options, level: 'success' });
|
||||
}
|
||||
|
||||
public warning(title: string, data: unknown = null, options?: Omit<LogOptions, 'level'>): void {
|
||||
this.log(title, data, { ...options, level: 'warning' });
|
||||
}
|
||||
|
||||
public error(title: string, data: unknown = null, options?: Omit<LogOptions, 'level'>): void {
|
||||
this.log(title, data, { ...options, level: 'error' });
|
||||
}
|
||||
|
||||
public debug(title: string, data: unknown = null, options?: Omit<LogOptions, 'level'>): void {
|
||||
this.log(title, data, { ...options, level: 'debug' });
|
||||
}
|
||||
|
||||
// Table logging
|
||||
public table(title: string, data: unknown[] = []): void {
|
||||
if (this.isProduction) return;
|
||||
|
||||
console.group(`%c${title}`, this.styles.info.title);
|
||||
if (data.length > 0) {
|
||||
console.table(data);
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
|
||||
// Performance logging
|
||||
public time(label: string): void {
|
||||
if (this.isProduction) return;
|
||||
console.time(label);
|
||||
}
|
||||
|
||||
public timeEnd(label: string): void {
|
||||
if (this.isProduction) return;
|
||||
console.timeEnd(label);
|
||||
}
|
||||
|
||||
// Group logging
|
||||
public group(title: string, collapsed = false): void {
|
||||
if (this.isProduction) return;
|
||||
|
||||
const method = collapsed ? console.groupCollapsed : console.group;
|
||||
method(`%c${title}`, this.styles.info.title);
|
||||
}
|
||||
|
||||
public groupEnd(): void {
|
||||
if (this.isProduction) return;
|
||||
console.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
// Create default instance
|
||||
const logger = new ConsoleLogger();
|
||||
export default logger;
|
||||
13
worklenz-frontend/src/utils/fetchData.ts
Normal file
13
worklenz-frontend/src/utils/fetchData.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
// function to fetch data
|
||||
export const fetchData = async (
|
||||
url: string,
|
||||
setState: React.Dispatch<React.SetStateAction<any[]>>
|
||||
) => {
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
const data = await response.json();
|
||||
setState(data);
|
||||
} catch (error) {
|
||||
console.error(`Error fetching data from ${url}:`, error);
|
||||
}
|
||||
};
|
||||
7
worklenz-frontend/src/utils/file-utils.ts
Normal file
7
worklenz-frontend/src/utils/file-utils.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const getBase64 = (file: File): Promise<string | ArrayBuffer | null> =>
|
||||
new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = error => reject(error);
|
||||
});
|
||||
12
worklenz-frontend/src/utils/format-date-time-with-locale.ts
Normal file
12
worklenz-frontend/src/utils/format-date-time-with-locale.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { format } from 'date-fns';
|
||||
import { enUS, es, pt } from 'date-fns/locale';
|
||||
import { getLanguageFromLocalStorage } from './language-utils';
|
||||
|
||||
export const formatDateTimeWithLocale = (dateString: string): string => {
|
||||
if (!dateString) return '';
|
||||
|
||||
const date = new Date(dateString);
|
||||
const localeString = getLanguageFromLocalStorage();
|
||||
const locale = localeString === 'en' ? enUS : localeString === 'es' ? es : pt;
|
||||
return format(date, 'MMM d, yyyy, h:mm:ss a', { locale });
|
||||
};
|
||||
10
worklenz-frontend/src/utils/get-initial-theme.ts
Normal file
10
worklenz-frontend/src/utils/get-initial-theme.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export const getInitialTheme = () => {
|
||||
try {
|
||||
return (
|
||||
localStorage.getItem('theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
|
||||
);
|
||||
} catch {
|
||||
return 'light';
|
||||
}
|
||||
};
|
||||
21
worklenz-frontend/src/utils/getPriorityColors.ts
Normal file
21
worklenz-frontend/src/utils/getPriorityColors.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { TaskPriorityType } from '../types/task.types';
|
||||
|
||||
type ThemeMode = 'light' | 'dark';
|
||||
|
||||
const priorityColors = {
|
||||
light: {
|
||||
low: '#c2e4d0',
|
||||
medium: '#f9e3b1',
|
||||
high: '#f6bfc0',
|
||||
},
|
||||
dark: {
|
||||
low: '#75c997',
|
||||
medium: '#fbc84c',
|
||||
high: '#f37070',
|
||||
},
|
||||
};
|
||||
|
||||
export const getPriorityColor = (priority: string, themeMode: ThemeMode): string => {
|
||||
const colors = priorityColors[themeMode];
|
||||
return colors[priority as TaskPriorityType];
|
||||
};
|
||||
21
worklenz-frontend/src/utils/getStatusColor.ts
Normal file
21
worklenz-frontend/src/utils/getStatusColor.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { TaskStatusType } from '../types/task.types';
|
||||
|
||||
type ThemeMode = 'light' | 'dark';
|
||||
|
||||
const statusColors = {
|
||||
light: {
|
||||
todo: '#d8d7d8',
|
||||
doing: '#c0d5f6',
|
||||
done: '#c2e4d0',
|
||||
},
|
||||
dark: {
|
||||
todo: '#a9a9a9',
|
||||
doing: '#70a6f3',
|
||||
done: '#75c997',
|
||||
},
|
||||
};
|
||||
|
||||
export const getStatusColor = (status: string, themeMode: ThemeMode): string => {
|
||||
const colors = statusColors[themeMode];
|
||||
return colors[status as TaskStatusType];
|
||||
};
|
||||
35
worklenz-frontend/src/utils/greetingString.ts
Normal file
35
worklenz-frontend/src/utils/greetingString.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { getLanguageFromLocalStorage } from './language-utils';
|
||||
|
||||
export const greetingString = (name: string): string => {
|
||||
const date = dayjs();
|
||||
const hours = date.hour();
|
||||
let greet;
|
||||
|
||||
if (hours < 12) greet = 'morning';
|
||||
else if (hours >= 12 && hours < 16) greet = 'afternoon';
|
||||
else if (hours >= 16 && hours < 24) greet = 'evening';
|
||||
|
||||
const language = getLanguageFromLocalStorage();
|
||||
let greetingPrefix = 'Hi';
|
||||
let greetingSuffix = 'Good';
|
||||
let morning = 'morning';
|
||||
let afternoon = 'afternoon';
|
||||
let evening = 'evening';
|
||||
|
||||
if (language === 'es') {
|
||||
greetingPrefix = 'Hola';
|
||||
greetingSuffix = 'Buen';
|
||||
morning = 'mañana';
|
||||
afternoon = 'tarde';
|
||||
evening = 'noche';
|
||||
} else if (language === 'pt') {
|
||||
greetingPrefix = 'Olá';
|
||||
greetingSuffix = 'Bom';
|
||||
morning = 'manhã';
|
||||
afternoon = 'tarde';
|
||||
evening = 'noite';
|
||||
}
|
||||
|
||||
return `${greetingPrefix} ${name}, ${greetingSuffix} ${greet}!`;
|
||||
};
|
||||
37
worklenz-frontend/src/utils/language-utils.ts
Normal file
37
worklenz-frontend/src/utils/language-utils.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { ILanguageType, Language } from '@/features/i18n/localesSlice';
|
||||
|
||||
const STORAGE_KEY = 'i18nextLng';
|
||||
|
||||
/**
|
||||
* Gets the user's browser language and returns it if supported, otherwise returns English
|
||||
* @returns The detected supported language or English as fallback
|
||||
*/
|
||||
export const getDefaultLanguage = (): ILanguageType => {
|
||||
const browserLang = navigator.language.split('-')[0];
|
||||
if (Object.values(Language).includes(browserLang as Language)) {
|
||||
return browserLang as ILanguageType;
|
||||
}
|
||||
return Language.EN;
|
||||
};
|
||||
|
||||
export const DEFAULT_LANGUAGE: ILanguageType = getDefaultLanguage();
|
||||
|
||||
/**
|
||||
* Gets the current language from local storage
|
||||
* @returns The stored language or default language if not found
|
||||
*/
|
||||
export const getLanguageFromLocalStorage = (): ILanguageType => {
|
||||
const savedLng = localStorage.getItem(STORAGE_KEY);
|
||||
if (Object.values(Language).includes(savedLng as Language)) {
|
||||
return savedLng as ILanguageType;
|
||||
}
|
||||
return DEFAULT_LANGUAGE;
|
||||
};
|
||||
|
||||
/**
|
||||
* Saves the current language to local storage
|
||||
* @param lng Language to save
|
||||
*/
|
||||
export const saveLanguageInLocalStorage = (lng: ILanguageType): void => {
|
||||
localStorage.setItem(STORAGE_KEY, lng);
|
||||
};
|
||||
17
worklenz-frontend/src/utils/localStorageFunctions.ts
Normal file
17
worklenz-frontend/src/utils/localStorageFunctions.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// these functions are utility functions which are use for save data and get data from local storage
|
||||
export const getJSONFromLocalStorage = (name: string) => {
|
||||
const storedItem = localStorage.getItem(name);
|
||||
return storedItem ? JSON.parse(storedItem) : null;
|
||||
};
|
||||
|
||||
export const saveJSONToLocalStorage = (name: string, item: unknown) => {
|
||||
localStorage.setItem(name, JSON.stringify(item));
|
||||
};
|
||||
|
||||
export const saveToLocalStorage = (name: string, item: string) => {
|
||||
localStorage.setItem(name, item);
|
||||
};
|
||||
|
||||
export const getFromLocalStorage = (name: string) => {
|
||||
return localStorage.getItem(name);
|
||||
};
|
||||
11
worklenz-frontend/src/utils/mixpanelInit.ts
Normal file
11
worklenz-frontend/src/utils/mixpanelInit.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { MixpanelConfig } from '@/types/mixpanel.types';
|
||||
import mixpanel from 'mixpanel-browser';
|
||||
|
||||
export const initMixpanel = (token: string, config: MixpanelConfig = {}): void => {
|
||||
mixpanel.init(token, {
|
||||
debug: import.meta.env.VITE_APP_ENV !== 'production',
|
||||
track_pageview: true,
|
||||
persistence: 'localStorage',
|
||||
...config,
|
||||
});
|
||||
};
|
||||
24
worklenz-frontend/src/utils/project-list-utils.ts
Normal file
24
worklenz-frontend/src/utils/project-list-utils.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { DATE_FORMAT_OPTIONS } from '@/shared/constants';
|
||||
import { IProjectViewModel } from '@/types/project/projectViewModel.types';
|
||||
|
||||
interface DateRange {
|
||||
startDate: string | null;
|
||||
endDate: string | null;
|
||||
}
|
||||
|
||||
export const formatDateRange = ({ startDate, endDate }: DateRange): string => {
|
||||
const formattedStart = startDate
|
||||
? new Date(startDate).toLocaleDateString('en-US', DATE_FORMAT_OPTIONS)
|
||||
: 'N/A';
|
||||
const formattedEnd = endDate
|
||||
? new Date(endDate).toLocaleDateString('en-US', DATE_FORMAT_OPTIONS)
|
||||
: 'N/A';
|
||||
|
||||
return `Start date: ${formattedStart}\nEnd date: ${formattedEnd}`;
|
||||
};
|
||||
|
||||
export const getTaskProgressTitle = (data: IProjectViewModel): string => {
|
||||
if (!data.all_tasks_count) return 'No tasks available.';
|
||||
if (data.all_tasks_count === data.completed_tasks_count) return 'All tasks completed.';
|
||||
return `${data.completed_tasks_count || 0}/${data.all_tasks_count || 0} tasks completed.`;
|
||||
};
|
||||
11
worklenz-frontend/src/utils/projectUtils.ts
Normal file
11
worklenz-frontend/src/utils/projectUtils.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { PROJECT_STATUS_ICON_MAP } from '@/shared/constants';
|
||||
import React from 'react';
|
||||
|
||||
export function getStatusIcon(statusIcon: string, colorCode: string) {
|
||||
return React.createElement(
|
||||
PROJECT_STATUS_ICON_MAP[statusIcon as keyof typeof PROJECT_STATUS_ICON_MAP],
|
||||
{
|
||||
style: { fontSize: 16, color: colorCode },
|
||||
}
|
||||
);
|
||||
}
|
||||
35
worklenz-frontend/src/utils/sanitizeInput.ts
Normal file
35
worklenz-frontend/src/utils/sanitizeInput.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import DOMPurify from 'dompurify';
|
||||
|
||||
/**
|
||||
* Sanitizes user input to prevent XSS attacks
|
||||
*
|
||||
* @param input - The user input string to sanitize
|
||||
* @param options - Optional configuration for DOMPurify
|
||||
* @returns Sanitized string
|
||||
*/
|
||||
export const sanitizeInput = (input: string, options?: DOMPurify.Config): string => {
|
||||
if (!input) return '';
|
||||
|
||||
// Default options for plain text inputs (strip all HTML)
|
||||
const defaultOptions: DOMPurify.Config = {
|
||||
ALLOWED_TAGS: [],
|
||||
ALLOWED_ATTR: [],
|
||||
};
|
||||
|
||||
return DOMPurify.sanitize(input, options || defaultOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Sanitizes a string for use in HTML contexts (allows some basic tags)
|
||||
*
|
||||
* @param input - The input containing HTML to sanitize
|
||||
* @returns Sanitized HTML string
|
||||
*/
|
||||
export const sanitizeHtml = (input: string): string => {
|
||||
if (!input) return '';
|
||||
|
||||
return DOMPurify.sanitize(input, {
|
||||
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
|
||||
ALLOWED_ATTR: ['href', 'target', 'rel'],
|
||||
});
|
||||
};
|
||||
3
worklenz-frontend/src/utils/schedule.ts
Normal file
3
worklenz-frontend/src/utils/schedule.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const getDayName = (date: Date) => {
|
||||
return date.toLocaleDateString('en-US', { weekday: 'long' }); // Returns `Monday`, `Tuesday`, etc.
|
||||
};
|
||||
33
worklenz-frontend/src/utils/session-helper.ts
Normal file
33
worklenz-frontend/src/utils/session-helper.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { ILocalSession } from '@/types/auth/local-session.types';
|
||||
|
||||
export const WORKLENZ_SESSION_ID = import.meta.env.VITE_WORKLENZ_SESSION_ID;
|
||||
const storage: Storage = localStorage;
|
||||
|
||||
export function setSession(user: ILocalSession): void {
|
||||
storage.setItem(WORKLENZ_SESSION_ID, btoa(unescape(encodeURIComponent(JSON.stringify(user)))));
|
||||
// storage.setItem(WORKLENZ_SESSION_ID, btoa(JSON.stringify(user)));
|
||||
}
|
||||
|
||||
export function getUserSession(): ILocalSession | null {
|
||||
try {
|
||||
return JSON.parse(atob(<string>storage.getItem(WORKLENZ_SESSION_ID)));
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function hasSession() {
|
||||
return !!storage.getItem(WORKLENZ_SESSION_ID);
|
||||
}
|
||||
|
||||
export function deleteSession() {
|
||||
storage.removeItem(WORKLENZ_SESSION_ID);
|
||||
}
|
||||
|
||||
export function getRole() {
|
||||
const session = getUserSession();
|
||||
if (!session) return 'Unknown';
|
||||
if (session.owner) return 'Owner';
|
||||
if (session.is_admin) return 'Admin';
|
||||
return 'Member';
|
||||
}
|
||||
24
worklenz-frontend/src/utils/simpleDateFormat.ts
Normal file
24
worklenz-frontend/src/utils/simpleDateFormat.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
export const simpleDateFormat = (date: Date | string | null): string => {
|
||||
if (!date) return '';
|
||||
|
||||
// convert ISO string date to Date object if necessary
|
||||
const dateObj = typeof date === 'string' ? new Date(date) : date;
|
||||
|
||||
// check if the date is valid
|
||||
if (isNaN(dateObj.getTime())) return '';
|
||||
|
||||
const options: Intl.DateTimeFormatOptions = {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
const inputYear = dateObj.getFullYear();
|
||||
|
||||
// add year to the format if it's not the current year
|
||||
if (inputYear !== currentYear) {
|
||||
options.year = 'numeric';
|
||||
}
|
||||
|
||||
return new Intl.DateTimeFormat('en-US', options).format(dateObj);
|
||||
};
|
||||
34
worklenz-frontend/src/utils/sort-team-members.ts
Normal file
34
worklenz-frontend/src/utils/sort-team-members.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
export const sortByBooleanField = <T extends Record<string, any>>(
|
||||
data: T[],
|
||||
field: keyof T,
|
||||
prioritizeTrue: boolean = true
|
||||
) => {
|
||||
return [...data].sort((a, b) => {
|
||||
const aValue = !!a[field];
|
||||
const bValue = !!b[field];
|
||||
|
||||
if (aValue === bValue) return 0;
|
||||
if (prioritizeTrue) {
|
||||
return aValue ? -1 : 1;
|
||||
} else {
|
||||
return !aValue ? -1 : 1;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const sortBySelection = (data: Array<{ selected?: boolean }>) =>
|
||||
sortByBooleanField(data, 'selected');
|
||||
|
||||
export const sortByPending = (data: Array<{ pending_invitation?: boolean }>) =>
|
||||
sortByBooleanField(data, 'pending_invitation', false);
|
||||
|
||||
export const sortTeamMembers = (data: Array<{ selected?: boolean; pending_invitation?: boolean; is_pending?: boolean }>) => {
|
||||
return sortByBooleanField(
|
||||
sortByBooleanField(
|
||||
sortByBooleanField(data, 'is_pending', false),
|
||||
'pending_invitation',
|
||||
false
|
||||
),
|
||||
'selected'
|
||||
);
|
||||
};
|
||||
12
worklenz-frontend/src/utils/test-utils.ts
Normal file
12
worklenz-frontend/src/utils/test-utils.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { configureStore } from '@reduxjs/toolkit';
|
||||
import authReducer from '@/features/auth/authSlice';
|
||||
import userReducer from '@/features/user/userSlice';
|
||||
export const mockStore = (preloadedState = {}) => {
|
||||
return configureStore({
|
||||
reducer: {
|
||||
auth: authReducer,
|
||||
user: userReducer,
|
||||
},
|
||||
preloadedState,
|
||||
});
|
||||
};
|
||||
6
worklenz-frontend/src/utils/themeWiseColor.ts
Normal file
6
worklenz-frontend/src/utils/themeWiseColor.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
type ThemeMode = 'light' | 'dark';
|
||||
|
||||
// this utility for toggle any colors with the theme
|
||||
export const themeWiseColor = (defaultColor: string, darkColor: string, themeMode: ThemeMode) => {
|
||||
return themeMode === 'dark' ? darkColor : defaultColor;
|
||||
};
|
||||
13
worklenz-frontend/src/utils/timeUtils.ts
Normal file
13
worklenz-frontend/src/utils/timeUtils.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export function formatDate(date: Date): string {
|
||||
return dayjs(date).format('MMM DD, YYYY');
|
||||
}
|
||||
|
||||
export function buildTimeString(hours: number, minutes: number, seconds: number) {
|
||||
const h = hours > 0 ? `${hours}h` : '';
|
||||
const m = `${minutes}m`;
|
||||
const s = `${seconds}s`;
|
||||
return `${h} ${m} ${s}`.trim();
|
||||
}
|
||||
|
||||
7
worklenz-frontend/src/utils/timeZoneCurrencyMap.ts
Normal file
7
worklenz-frontend/src/utils/timeZoneCurrencyMap.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const timeZoneCurrencyMap: { [key: string]: string } = {
|
||||
'America/New_York': 'USD', // United States Dollar
|
||||
'Europe/London': 'GBP', // British Pound
|
||||
'Asia/Tokyo': 'JPY', // Japanese Yen
|
||||
'Asia/Colombo': 'Rs', // Sri Lankan Rupee
|
||||
'Asia/Kolkata': 'INR', // Indian Rupee
|
||||
};
|
||||
3
worklenz-frontend/src/utils/toCamelCase.ts
Normal file
3
worklenz-frontend/src/utils/toCamelCase.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const toCamelCase = (str: string) => {
|
||||
return str.toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (_, char) => char.toUpperCase());
|
||||
};
|
||||
9
worklenz-frontend/src/utils/toQueryString.ts
Normal file
9
worklenz-frontend/src/utils/toQueryString.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export function toQueryString(obj: any) {
|
||||
const query = [];
|
||||
for (const key in obj) {
|
||||
if (typeof obj[key] !== undefined && obj[key] !== null) {
|
||||
query.push(`${key}=${obj[key]}`);
|
||||
}
|
||||
}
|
||||
return '?' + query.join('&');
|
||||
}
|
||||
12
worklenz-frontend/src/utils/validateEmail.ts
Normal file
12
worklenz-frontend/src/utils/validateEmail.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export const validateEmail = (email: string): boolean => {
|
||||
if (!email) return false;
|
||||
|
||||
// Check if the email has basic format with @ and domain part
|
||||
if (!email.includes('@') || email.endsWith('@') || email.split('@').length !== 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const EMAIL_REGEXP =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return EMAIL_REGEXP.test(email);
|
||||
};
|
||||
Reference in New Issue
Block a user