Files
tm_panel/src/utils/shared.ts
T
AmirReza Jamali f84c21f829 feat(form): Add clearable functionality to Select component
This commit introduces a new `clearable` prop to the `Select` component, allowing users to easily reset its value.

When the `clearable` prop is set to true, a clear button (an 'X' icon) is displayed when an option is selected. Clicking this button resets the select input to its initial placeholder state and triggers an optional `onClear` callback.

To support this, the component's internal state management was refactored to control the input's value.

Additionally, the `AdminListFilters` component has been updated to:
- Utilize the new clearable select for the status filter.
- Consolidate multiple search fields (email, username, full_name) into a single generic "search" input.
- Add explicit "Reset" and "Apply" buttons for better filter management.
2025-10-13 12:26:56 +03:30

113 lines
2.6 KiB
TypeScript

import { ILabelValue } from "@/types/shared";
import * as moment from "moment";
import { z } from "zod";
export const unixToDate = ({
hasTime = false,
unix,
}: {
unix: number;
hasTime?: boolean;
}) => {
return moment.unix(unix).format(`YYYY/MM/DD ${hasTime ? "HH:MM" : ""}`);
};
export const msToDate = (ms: number) => {
return moment.default(ms).format("YYYY/MM/DD");
};
export const isoStringToDate = ({
hasTime = false,
isoString,
}: {
isoString: string;
hasTime: boolean;
}): string => {
return moment
.default(isoString)
.format(`YYYY/MM/DD ${hasTime ? "HH:MM" : ""}`);
};
export const getStatusColor = <T>(status: T) => {
switch (status) {
case "active":
return "bg-green text-white";
case "expired":
return "bg-orange-light text-white";
case "cancelled":
return "bg-error-dark text-white";
case "awarded":
return "bg-blue-light-3";
case "draft":
return "bg-gray-3";
}
};
export const formatPhoneNumber = (input?: string) => {
if (!input) return "";
let cleaned = input.replace(/[^\d+]/g, "");
const hasPlus = cleaned.startsWith("+");
if (hasPlus) {
cleaned = cleaned.slice(1);
}
const groups = [];
let i = 0;
while (i < cleaned.length) {
const size = i === 0 ? 3 : 4;
groups.push(cleaned.slice(i, i + size));
i += size;
}
return (hasPlus ? "+" : "") + groups.join(" ");
};
export const capitalize = (text: string) => {
return text
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join(" ")
.trim();
};
export const getEnumAsArray = <T extends object>(target: T): ILabelValue[] => {
return Object.entries(target).map(([key, value]) => ({
label: capitalize(key.split("_").join(" ")),
value,
}));
};
export const truncateString = (
str: string,
startLength: number = 10,
endLength: number = 10,
): string => {
if (str.length <= startLength + endLength) {
return str;
}
return str.slice(0, startLength) + "..." + str.slice(-endLength);
};
export function parseWithSchema<T>(
schema: z.ZodSchema<T>,
data: unknown,
errorContext: string,
): T {
const parsedData = schema.safeParse(data);
if (parsedData.success) {
return parsedData.data;
} else {
console.error(`ERROR caught in ${errorContext}`);
throw new Error(`ERROR caught in ${errorContext}`);
}
}
export const deleteEmptyKeys = (obj: Record<string, any>) => {
const newObj = { ...obj };
for (const key in newObj) {
if (Object.prototype.hasOwnProperty.call(newObj, key)) {
if (!newObj[key]) {
delete newObj[key];
}
}
}
return newObj;
};