first commit
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "@lucky-wheel/shared-types",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc -w"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.4.5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* تبدیل تمامی ارقام انگلیسی (0-9) و عربی (٠-٩) به ارقام استاندارد فارسی (۰-۹)
|
||||
*/
|
||||
const FARSI_DIGITS = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
|
||||
|
||||
export function toFarsiDigits(val: number | string | null | undefined): string {
|
||||
if (val === null || val === undefined) return '';
|
||||
return String(val)
|
||||
.replace(/[0-9]/g, (d) => FARSI_DIGITS[+d])
|
||||
.replace(/[٠-٩]/g, (d) => FARSI_DIGITS[d.charCodeAt(0) - 1632]);
|
||||
}
|
||||
|
||||
/**
|
||||
* قالببندی اعداد با جداکننده هزارگان و ارقام فارسی
|
||||
*/
|
||||
export function formatFarsiNumber(val: number | string | null | undefined): string {
|
||||
if (val === null || val === undefined || val === '') return '';
|
||||
const num = typeof val === 'string' ? parseFloat(toEnglishDigits(val)) : val;
|
||||
if (isNaN(num)) return toFarsiDigits(val);
|
||||
const formatted = new Intl.NumberFormat('fa-IR').format(num);
|
||||
return toFarsiDigits(formatted);
|
||||
}
|
||||
|
||||
/**
|
||||
* تبدیل ارقام فارسی (۰-۹) و عربی (٠-٩) به ارقام استاندارد انگلیسی (0-9)
|
||||
*/
|
||||
export function toEnglishDigits(val: number | string | null | undefined): string {
|
||||
if (val === null || val === undefined) return '';
|
||||
return String(val)
|
||||
.replace(/[۰-۹]/g, (d) => String(d.charCodeAt(0) - 1776))
|
||||
.replace(/[٠-٩]/g, (d) => String(d.charCodeAt(0) - 1632));
|
||||
}
|
||||
|
||||
/**
|
||||
* پارس مطمئن مقادیر عددی از ورودیها (پشتیبانی از هر دو فرمت فارسی و انگلیسی)
|
||||
*/
|
||||
export function parseFarsiNumber(val: number | string | null | undefined, fallback: number = 0): number {
|
||||
if (val === null || val === undefined || val === '') return fallback;
|
||||
if (typeof val === 'number') return isNaN(val) ? fallback : val;
|
||||
const enStr = toEnglishDigits(val).replace(/[^0-9.-]/g, '');
|
||||
const parsed = Number(enStr);
|
||||
return isNaN(parsed) ? fallback : parsed;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
// ==========================================
|
||||
// Shared Enums (Synchronized with Prisma Schema)
|
||||
// ==========================================
|
||||
|
||||
export enum AdminStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
DISABLED = 'DISABLED',
|
||||
}
|
||||
|
||||
export enum AdminRole {
|
||||
SUPER_ADMIN = 'SUPER_ADMIN',
|
||||
OPERATOR = 'OPERATOR',
|
||||
SUPPORT = 'SUPPORT',
|
||||
}
|
||||
|
||||
export enum UserStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
BLOCKED = 'BLOCKED',
|
||||
}
|
||||
|
||||
export enum CampaignStatus {
|
||||
DRAFT = 'DRAFT',
|
||||
ACTIVE = 'ACTIVE',
|
||||
PAUSED = 'PAUSED',
|
||||
FINISHED = 'FINISHED',
|
||||
}
|
||||
|
||||
export enum PrizeType {
|
||||
PHYSICAL = 'PHYSICAL',
|
||||
DISCOUNT_CODE = 'DISCOUNT_CODE',
|
||||
CREDIT = 'CREDIT',
|
||||
CASH = 'CASH',
|
||||
NO_PRIZE = 'NO_PRIZE',
|
||||
OTHER = 'OTHER',
|
||||
}
|
||||
|
||||
export enum StockType {
|
||||
LIMITED = 'LIMITED',
|
||||
UNLIMITED = 'UNLIMITED',
|
||||
}
|
||||
|
||||
export enum PrizeStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
INACTIVE = 'INACTIVE',
|
||||
}
|
||||
|
||||
export enum SpinStatus {
|
||||
SUCCESS = 'SUCCESS',
|
||||
FAILED = 'FAILED',
|
||||
DUPLICATE = 'DUPLICATE',
|
||||
}
|
||||
|
||||
export enum ChanceTxnType {
|
||||
SIGNUP_BONUS = 'SIGNUP_BONUS',
|
||||
ADMIN_ADJUSTMENT = 'ADMIN_ADJUSTMENT',
|
||||
SPIN_CONSUME = 'SPIN_CONSUME',
|
||||
CAMPAIGN_BONUS = 'CAMPAIGN_BONUS',
|
||||
REFUND = 'REFUND',
|
||||
}
|
||||
|
||||
export enum UserPrizeStatus {
|
||||
NEW = 'NEW',
|
||||
CONTACTED = 'CONTACTED',
|
||||
DELIVERED = 'DELIVERED',
|
||||
ACTIVE_CODE = 'ACTIVE_CODE',
|
||||
USED = 'USED',
|
||||
EXPIRED = 'EXPIRED',
|
||||
}
|
||||
|
||||
export enum SmsType {
|
||||
OTP = 'OTP',
|
||||
WINNING = 'WINNING',
|
||||
NO_PRIZE = 'NO_PRIZE',
|
||||
PRIZE_REMINDER = 'PRIZE_REMINDER',
|
||||
}
|
||||
|
||||
export enum SmsStatus {
|
||||
PENDING = 'PENDING',
|
||||
SENT = 'SENT',
|
||||
FAILED = 'FAILED',
|
||||
}
|
||||
|
||||
export enum WheelDesign {
|
||||
CLASSIC_CASINO = 'CLASSIC_CASINO',
|
||||
NEON_CYBER = 'NEON_CYBER',
|
||||
MINIMAL_FLAT = 'MINIMAL_FLAT',
|
||||
LUXURY_GOLD = 'LUXURY_GOLD',
|
||||
PLAYFUL_CARTOON = 'PLAYFUL_CARTOON',
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Client & Wheel Interfaces
|
||||
// ==========================================
|
||||
|
||||
export interface WheelSegment {
|
||||
id: string;
|
||||
title: string;
|
||||
image?: string | null;
|
||||
color: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
export interface WheelDesignProps {
|
||||
segments: WheelSegment[];
|
||||
targetSegmentIndex: number | null;
|
||||
spinning: boolean;
|
||||
}
|
||||
|
||||
export interface PublicCampaignWheelDto {
|
||||
id: string;
|
||||
title: string;
|
||||
slug: string;
|
||||
description: string | null;
|
||||
logo: string | null;
|
||||
banner: string | null;
|
||||
wheelDesign: WheelDesign;
|
||||
settings: Record<string, any> | null;
|
||||
rules: string | null;
|
||||
ctaText: string | null;
|
||||
startAt: string;
|
||||
endAt: string;
|
||||
segments: WheelSegment[];
|
||||
}
|
||||
|
||||
export interface SpinRequestDto {
|
||||
idempotencyKey: string;
|
||||
}
|
||||
|
||||
export interface PrizeResultDto {
|
||||
id: string;
|
||||
title: string;
|
||||
segment: number;
|
||||
code: string | null;
|
||||
type: PrizeType;
|
||||
image: string | null;
|
||||
}
|
||||
|
||||
export interface SpinResponseDto {
|
||||
success: boolean;
|
||||
spinId: string;
|
||||
prize: PrizeResultDto | null;
|
||||
remainingChances: number;
|
||||
}
|
||||
|
||||
export interface UserChancesDto {
|
||||
totalChances: number;
|
||||
usedChances: number;
|
||||
remainingChances: number;
|
||||
}
|
||||
|
||||
export interface UserPrizeItemDto {
|
||||
id: string;
|
||||
prizeTitle: string;
|
||||
prizeImage: string | null;
|
||||
prizeType: PrizeType;
|
||||
code: string | null;
|
||||
status: UserPrizeStatus;
|
||||
wonAt: string;
|
||||
expiresAt: string | null;
|
||||
campaignTitle: string;
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Admin & Reporting Interfaces
|
||||
// ==========================================
|
||||
|
||||
export interface PaginationMeta {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
export interface PaginatedResponse<T> {
|
||||
data: T[];
|
||||
meta: PaginationMeta;
|
||||
}
|
||||
|
||||
export interface DashboardStatsDto {
|
||||
totalUsers: number;
|
||||
todayUsers: number;
|
||||
totalSpins: number;
|
||||
todaySpins: number;
|
||||
totalWinners: number;
|
||||
totalDistributedPrizes: number;
|
||||
consumedChances: number;
|
||||
activePrizesCount: number;
|
||||
}
|
||||
|
||||
export interface SpinsByDayDataPoint {
|
||||
date: string;
|
||||
spins: number;
|
||||
winners: number;
|
||||
}
|
||||
|
||||
export interface PrizeDistributionDataPoint {
|
||||
prizeTitle: string;
|
||||
count: number;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export * from './farsi-number';
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src"
|
||||
},
|
||||
"include": ["src/**/*"]
|
||||
}
|
||||
Reference in New Issue
Block a user