@yangsaiyam/helper 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # korre-mapping
2
+
3
+ Shared key-value mappings for Korre projects.
4
+
5
+ ## Install
6
+
7
+ SSH:
8
+
9
+ ```bash
10
+ pnpm add git+ssh://git@github.com:your-org/korre-mapping.git
11
+ ```
12
+
13
+ HTTPS:
14
+
15
+ ```bash
16
+ pnpm add github:your-org/korre-mapping
17
+ ```
18
+
19
+ ## Use
20
+
21
+ ```ts
22
+ import { CAREER_LOCATION, getLabelByKey } from "@korre/mapping";
23
+
24
+ const label = getLabelByKey(CAREER_LOCATION, 1);
25
+ // KualaLumpur
26
+ ```
27
+
28
+ ```tsx
29
+ import { CAREER_JOB_TYPE, getOptionByKey } from "@korre/mapping";
30
+
31
+ type CareerCardProps = {
32
+ career: {
33
+ title: string;
34
+ jobType: number;
35
+ };
36
+ };
37
+
38
+ export default function CareerCard({ career }: CareerCardProps) {
39
+ const jobType = getOptionByKey(CAREER_JOB_TYPE, career.jobType);
40
+
41
+ return (
42
+ <div>
43
+ <h2>{career.title}</h2>
44
+ <p>{jobType?.value ?? "Unknown"}</p>
45
+ </div>
46
+ );
47
+ }
48
+ ```
49
+
50
+ ## Structure
51
+
52
+ ```text
53
+ src/
54
+ ├─ career/
55
+ │ └─ index.ts
56
+ ├─ contact-form/
57
+ │ └─ index.ts
58
+ ├─ files/
59
+ │ └─ index.ts
60
+ ├─ shared/
61
+ │ ├─ helpers.ts
62
+ │ └─ types.ts
63
+ └─ index.ts
64
+ ```
65
+
66
+ ## Exports
67
+
68
+ - `CAREER_JOB_TYPE`
69
+ - `CAREER_CATEGORY`
70
+ - `CAREER_MODE`
71
+ - `CAREER_LOCATION`
72
+ - `CONTACT_FORM_ENQUIRY_TYPE`
73
+ - `FILE_STATUS`
74
+ - `FILE_ORIGIN`
75
+ - `getOptionByKey`
76
+ - `getLabelByKey`
77
+ - `getI18nKeyByKey`
78
+ - `toRecord`
79
+
80
+ ## Update mappings
81
+
82
+ Update `src/index.ts`, then commit and push:
83
+
84
+ ```bash
85
+ git add .
86
+ git commit -m "update mappings"
87
+ git push
88
+ ```
89
+
90
+ In consuming repos:
91
+
92
+ ```bash
93
+ pnpm update @korre/mapping
94
+ ```
package/package.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@yangsaiyam/helper",
3
+ "version": "1.1.0",
4
+ "main": "./src/index.ts",
5
+ "types": "./src/index.ts",
6
+ "exports": {
7
+ ".": "./src/index.ts"
8
+ }
9
+ }
@@ -0,0 +1,39 @@
1
+ import type { MappingOption } from "../shared/types";
2
+
3
+ export const CAREER_JOB_TYPE = [
4
+ { key: 1, value: "Full-time", i18nKey: "career.jobType.1" },
5
+ { key: 2, value: "Part-time", i18nKey: "career.jobType.2" },
6
+ { key: 3, value: "Contract", i18nKey: "career.jobType.3" },
7
+ { key: 4, value: "Internship", i18nKey: "career.jobType.4" },
8
+ ] as const satisfies readonly MappingOption[];
9
+
10
+ export const CAREER_CATEGORY = [
11
+ { key: 1, value: "Engineering", i18nKey: "career.category.1" },
12
+ { key: 2, value: "Design", i18nKey: "career.category.2" },
13
+ { key: 3, value: "Product", i18nKey: "career.category.3" },
14
+ { key: 4, value: "Marketing", i18nKey: "career.category.4" },
15
+ ] as const satisfies readonly MappingOption[];
16
+
17
+ export const CAREER_MODE = [
18
+ { key: 1, value: "On-site", i18nKey: "career.mode.1" },
19
+ { key: 2, value: "Remote", i18nKey: "career.mode.2" },
20
+ { key: 3, value: "Hybrid", i18nKey: "career.mode.3" },
21
+ ] as const satisfies readonly MappingOption[];
22
+
23
+ export const CAREER_LOCATION = [
24
+ { key: 1, value: "KualaLumpur", i18nKey: "career.location.1" },
25
+ { key: 2, value: "Selangor", i18nKey: "career.location.2" },
26
+ { key: 3, value: "Johor", i18nKey: "career.location.3" },
27
+ { key: 4, value: "Penang", i18nKey: "career.location.4" },
28
+ { key: 5, value: "Sabah", i18nKey: "career.location.5" },
29
+ { key: 6, value: "Sarawak", i18nKey: "career.location.6" },
30
+ { key: 7, value: "NegeriSembilan", i18nKey: "career.location.7" },
31
+ { key: 8, value: "Kedah", i18nKey: "career.location.8" },
32
+ { key: 9, value: "Kelantan", i18nKey: "career.location.9" },
33
+ { key: 10, value: "Perak", i18nKey: "career.location.10" },
34
+ { key: 11, value: "Terengganu", i18nKey: "career.location.11" },
35
+ { key: 12, value: "Perlis", i18nKey: "career.location.12" },
36
+ { key: 13, value: "Melaka", i18nKey: "career.location.13" },
37
+ { key: 14, value: "Putrajaya", i18nKey: "career.location.14" },
38
+ { key: 15, value: "Labuan", i18nKey: "career.location.15" },
39
+ ] as const satisfies readonly MappingOption[];
@@ -0,0 +1,15 @@
1
+ import type { MappingOption } from "../shared/types";
2
+
3
+ export const CONTACT_FORM_ENQUIRY_TYPE = [
4
+ {
5
+ key: 1,
6
+ value: "Software Development",
7
+ i18nKey: "contactForm.enquiryType.1",
8
+ },
9
+ {
10
+ key: 2,
11
+ value: "Advertising Platform",
12
+ i18nKey: "contactForm.enquiryType.2",
13
+ },
14
+ { key: 3, value: "MCN Business", i18nKey: "contactForm.enquiryType.3" },
15
+ ] as const satisfies readonly MappingOption[];
@@ -0,0 +1,11 @@
1
+ import type { MappingOption } from "../shared/types";
2
+
3
+ export const FILE_STATUS = [
4
+ { key: 1, value: "pending", i18nKey: "files.status.1" },
5
+ { key: 2, value: "submitted", i18nKey: "files.status.2" },
6
+ ] as const satisfies readonly MappingOption[];
7
+
8
+ export const FILE_ORIGIN = [
9
+ { key: 1, value: "job applications", i18nKey: "files.origin.1" },
10
+ { key: 2, value: "submitted", i18nKey: "files.origin.2" },
11
+ ] as const satisfies readonly MappingOption[];
package/src/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./career";
2
+ export * from "./contact-form";
3
+ export * from "./files";
4
+ export * from "./roles";
5
+ export * from "./shared/helpers";
6
+ export * from "./shared/types";
@@ -0,0 +1,59 @@
1
+ import assert from "node:assert/strict";
2
+
3
+ import {
4
+ PERMISSION_I18N_KEY,
5
+ Permission,
6
+ ROLE,
7
+ ROLE_PERMISSIONS,
8
+ hasPermission,
9
+ normalizeRole,
10
+ permissionLabelKey,
11
+ roleLabelKey,
12
+ roleToId,
13
+ } from "./index.ts";
14
+
15
+ assert.equal(normalizeRole(0), "super-admin");
16
+ assert.equal(normalizeRole("admin"), "admin");
17
+ assert.equal(normalizeRole(2), "staff");
18
+ assert.equal(normalizeRole("hr"), "hr");
19
+ assert.equal(normalizeRole("unknown"), undefined);
20
+ assert.equal(normalizeRole(undefined), undefined);
21
+
22
+ assert.equal(roleToId("super-admin"), 0);
23
+ assert.equal(roleToId("admin"), 1);
24
+ assert.equal(roleToId("staff"), 2);
25
+ assert.equal(roleToId("hr"), 3);
26
+ assert.equal(roleToId("unknown"), undefined);
27
+
28
+ assert.equal(roleLabelKey(0), "users.roleSuperAdmin");
29
+ assert.equal(roleLabelKey("admin"), "users.roleAdmin");
30
+ assert.equal(roleLabelKey(2), "users.roleStaff");
31
+ assert.equal(roleLabelKey("hr"), "users.roleHr");
32
+ assert.equal(roleLabelKey(undefined), "users.roleStaff");
33
+
34
+ assert.deepEqual(ROLE[0], {
35
+ key: 0,
36
+ value: "super-admin",
37
+ i18nKey: "users.roleSuperAdmin",
38
+ });
39
+ assert.equal(ROLE[3]?.i18nKey, "users.roleHr");
40
+
41
+ assert.equal(
42
+ PERMISSION_I18N_KEY[Permission.ALLOWED_EMAILS_READ],
43
+ "permissions.allowedEmailsRead",
44
+ );
45
+ assert.equal(
46
+ permissionLabelKey(Permission.CONTACT_FORM_DELETE),
47
+ "permissions.contactFormDelete",
48
+ );
49
+
50
+ assert.ok(ROLE_PERMISSIONS["super-admin"].includes(Permission.USERS_DELETE));
51
+ assert.ok(ROLE_PERMISSIONS.admin.includes(Permission.CONTACT_FORM_DELETE));
52
+ assert.ok(ROLE_PERMISSIONS.hr.includes(Permission.JOB_APPLICATIONS_READ));
53
+ assert.deepEqual(ROLE_PERMISSIONS.staff, [Permission.CAREERS_READ]);
54
+
55
+ assert.equal(hasPermission("super-admin", Permission.USERS_WRITE), true);
56
+ assert.equal(hasPermission(1, Permission.USERS_WRITE), false);
57
+ assert.equal(hasPermission("hr", Permission.CAREERS_WRITE), true);
58
+ assert.equal(hasPermission("staff", Permission.CAREERS_WRITE), false);
59
+ assert.equal(hasPermission(undefined, Permission.CAREERS_READ), false);
@@ -0,0 +1,144 @@
1
+ import type { MappingOption } from "../shared/types";
2
+
3
+ export type CanonicalRole = "super-admin" | "admin" | "hr" | "staff";
4
+ export type RoleValue = CanonicalRole | number;
5
+ export type RoleInput = RoleValue | string;
6
+
7
+ export const ROLE = [
8
+ { key: 0, value: "super-admin", i18nKey: "users.roleSuperAdmin" },
9
+ { key: 1, value: "admin", i18nKey: "users.roleAdmin" },
10
+ { key: 2, value: "staff", i18nKey: "users.roleStaff" },
11
+ { key: 3, value: "hr", i18nKey: "users.roleHr" },
12
+ ] as const satisfies readonly MappingOption[];
13
+
14
+ export function normalizeRole(
15
+ role: RoleInput | undefined,
16
+ ): CanonicalRole | undefined {
17
+ switch (role) {
18
+ case 0:
19
+ case "super-admin":
20
+ return "super-admin";
21
+ case 1:
22
+ case "admin":
23
+ return "admin";
24
+ case 2:
25
+ case "staff":
26
+ return "staff";
27
+ case 3:
28
+ case "hr":
29
+ return "hr";
30
+ default:
31
+ return undefined;
32
+ }
33
+ }
34
+
35
+ export function roleLabelKey(role: RoleInput | undefined): string {
36
+ switch (normalizeRole(role)) {
37
+ case "super-admin":
38
+ return "users.roleSuperAdmin";
39
+ case "admin":
40
+ return "users.roleAdmin";
41
+ case "hr":
42
+ return "users.roleHr";
43
+ case "staff":
44
+ return "users.roleStaff";
45
+ default:
46
+ return "users.roleStaff";
47
+ }
48
+ }
49
+
50
+ export function roleToId(role: RoleInput | undefined): number | undefined {
51
+ switch (normalizeRole(role)) {
52
+ case "super-admin":
53
+ return 0;
54
+ case "admin":
55
+ return 1;
56
+ case "staff":
57
+ return 2;
58
+ case "hr":
59
+ return 3;
60
+ default:
61
+ return undefined;
62
+ }
63
+ }
64
+
65
+ export const Permission = {
66
+ ALLOWED_EMAILS_READ: "allowed-emails:read",
67
+ ALLOWED_EMAILS_WRITE: "allowed-emails:write",
68
+ USERS_READ: "users:read",
69
+ USERS_WRITE: "users:write",
70
+ USERS_DELETE: "users:delete",
71
+ CAREERS_READ: "careers:read",
72
+ CAREERS_WRITE: "careers:write",
73
+ CAREERS_DELETE: "careers:delete",
74
+ CONTACT_FORM_READ: "contact-form:read",
75
+ CONTACT_FORM_DELETE: "contact-form:delete",
76
+ JOB_APPLICATIONS_READ: "job-applications:read",
77
+ } as const;
78
+
79
+ export type Permission = (typeof Permission)[keyof typeof Permission];
80
+
81
+ export const PERMISSION_I18N_KEY: Record<Permission, string> = {
82
+ [Permission.ALLOWED_EMAILS_READ]: "permissions.allowedEmailsRead",
83
+ [Permission.ALLOWED_EMAILS_WRITE]: "permissions.allowedEmailsWrite",
84
+ [Permission.USERS_READ]: "permissions.usersRead",
85
+ [Permission.USERS_WRITE]: "permissions.usersWrite",
86
+ [Permission.USERS_DELETE]: "permissions.usersDelete",
87
+ [Permission.CAREERS_READ]: "permissions.careersRead",
88
+ [Permission.CAREERS_WRITE]: "permissions.careersWrite",
89
+ [Permission.CAREERS_DELETE]: "permissions.careersDelete",
90
+ [Permission.CONTACT_FORM_READ]: "permissions.contactFormRead",
91
+ [Permission.CONTACT_FORM_DELETE]: "permissions.contactFormDelete",
92
+ [Permission.JOB_APPLICATIONS_READ]: "permissions.jobApplicationsRead",
93
+ };
94
+
95
+ export const ROLE_PERMISSIONS: Record<CanonicalRole, Permission[]> = {
96
+ "super-admin": [
97
+ Permission.ALLOWED_EMAILS_READ,
98
+ Permission.ALLOWED_EMAILS_WRITE,
99
+ Permission.USERS_READ,
100
+ Permission.USERS_WRITE,
101
+ Permission.USERS_DELETE,
102
+ Permission.CAREERS_READ,
103
+ Permission.CAREERS_WRITE,
104
+ Permission.CAREERS_DELETE,
105
+ Permission.CONTACT_FORM_READ,
106
+ Permission.CONTACT_FORM_DELETE,
107
+ Permission.JOB_APPLICATIONS_READ,
108
+ ],
109
+ admin: [
110
+ Permission.ALLOWED_EMAILS_READ,
111
+ Permission.ALLOWED_EMAILS_WRITE,
112
+ Permission.USERS_READ,
113
+ Permission.CAREERS_READ,
114
+ Permission.CAREERS_WRITE,
115
+ Permission.CAREERS_DELETE,
116
+ Permission.CONTACT_FORM_READ,
117
+ Permission.CONTACT_FORM_DELETE,
118
+ Permission.JOB_APPLICATIONS_READ,
119
+ ],
120
+ hr: [
121
+ Permission.CAREERS_READ,
122
+ Permission.CAREERS_WRITE,
123
+ Permission.CAREERS_DELETE,
124
+ Permission.JOB_APPLICATIONS_READ,
125
+ ],
126
+ staff: [Permission.CAREERS_READ],
127
+ };
128
+
129
+ export function hasPermission(
130
+ role: RoleInput | undefined,
131
+ permission: Permission,
132
+ ): boolean {
133
+ const normalizedRole = normalizeRole(role);
134
+
135
+ if (!normalizedRole) {
136
+ return false;
137
+ }
138
+
139
+ return ROLE_PERMISSIONS[normalizedRole].includes(permission);
140
+ }
141
+
142
+ export function permissionLabelKey(permission: Permission): string {
143
+ return PERMISSION_I18N_KEY[permission];
144
+ }
@@ -0,0 +1,34 @@
1
+ import type { MappingOption } from "./types";
2
+
3
+ export function getOptionByKey<T extends MappingOption>(
4
+ options: readonly T[],
5
+ key?: number | null,
6
+ ): T | undefined {
7
+ if (key === null || key === undefined) {
8
+ return undefined;
9
+ }
10
+
11
+ return options.find((option) => option.key === key);
12
+ }
13
+
14
+ export function getLabelByKey<T extends MappingOption>(
15
+ options: readonly T[],
16
+ key?: number | null,
17
+ fallback = "Unknown",
18
+ ): string {
19
+ return getOptionByKey(options, key)?.value ?? fallback;
20
+ }
21
+
22
+ export function getI18nKeyByKey<T extends MappingOption>(
23
+ options: readonly T[],
24
+ key?: number | null,
25
+ fallback = "unknown",
26
+ ): string {
27
+ return getOptionByKey(options, key)?.i18nKey ?? fallback;
28
+ }
29
+
30
+ export function toRecord<T extends MappingOption>(
31
+ options: readonly T[],
32
+ ): Record<number, string> {
33
+ return Object.fromEntries(options.map((option) => [option.key, option.value]));
34
+ }
@@ -0,0 +1,5 @@
1
+ export type MappingOption<K extends number = number> = {
2
+ key: K;
3
+ value: string;
4
+ i18nKey: string;
5
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Bundler",
6
+ "strict": true,
7
+ "declaration": true,
8
+ "skipLibCheck": true
9
+ },
10
+ "include": ["src"]
11
+ }