create-mobile-app 0.0.0-stage → 1.0.1

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/dist/cli.js ADDED
@@ -0,0 +1,2119 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";var Ee=Object.create;var U=Object.defineProperty;var Re=Object.getOwnPropertyDescriptor;var Ie=Object.getOwnPropertyNames;var Oe=Object.getPrototypeOf,Me=Object.prototype.hasOwnProperty;var Le=(e,t)=>{for(var o in t)U(e,o,{get:t[o],enumerable:!0})},ie=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Ie(t))!Me.call(e,i)&&i!==o&&U(e,i,{get:()=>t[i],enumerable:!(n=Re(t,i))||n.enumerable});return e};var x=(e,t,o)=>(o=e!=null?Ee(Oe(e)):{},ie(t||!e||!e.__esModule?U(o,"default",{value:e,enumerable:!0}):o,e)),He=e=>ie(U({},"__esModule",{value:!0}),e);var Ve={};Le(Ve,{runCli:()=>Be});module.exports=He(Ve);var $e=require("commander");var F=x(require("prompts"));var ae=x(require("path"));function L(e){if(!e||e.trim()==="")return{valid:!1,error:"Project name cannot be empty."};let t=e.trim();return t.includes("/")||t.includes("\\")||t.includes("..")?{valid:!1,error:'Project name cannot contain path separators or "..".'}:/^[a-zA-Z0-9_-]+$/.test(t)?["react","react-native","expo","node_modules","favicon.ico","package.json","tsconfig.json"].includes(t.toLowerCase())?{valid:!1,error:`"${t}" is a reserved system or package name.`}:{valid:!0,sanitizedName:t}:{valid:!1,error:"Project name may only contain letters, numbers, underscores, and hyphens."}}function _(e,t){return ae.default.resolve(e,t)}var j=x(require("fs")),se=x(require("path"));function Y(e){return j.default.existsSync(e)}function ce(e){return j.default.existsSync(e)?j.default.readdirSync(e).length===0:!0}function N(e){j.default.existsSync(e)||j.default.mkdirSync(e,{recursive:!0})}function r(e,t){let o=se.default.dirname(e);N(o),j.default.writeFileSync(e,t.trimStart(),"utf-8")}function le(e){j.default.existsSync(e)&&j.default.rmSync(e,{recursive:!0,force:!0})}var pe=require("child_process");function me(){let e=process.env.npm_config_user_agent||"";return e.includes("bun")?"bun":e.includes("pnpm")?"pnpm":e.includes("yarn")?"yarn":"npm"}function de(e,t){try{let o=t==="yarn"?"yarn install":t==="npm"?"npm install --legacy-peer-deps":`${t} install`;return(0,pe.execSync)(o,{cwd:e,stdio:"inherit"}),!0}catch{return!1}}var v=x(require("picocolors")),s={banner(){console.log(""),console.log(v.default.cyan(v.default.bold("\u{1F680} Create Mobile App"))),console.log(v.default.gray(` Production-ready React Native architecture in one command
4
+ `))},info(e){console.log(`${v.default.blue("\u2139")} ${e}`)},success(e){console.log(`${v.default.green("\u2713")} ${e}`)},step(e){console.log(`${v.default.cyan("\u26A1")} ${e}`)},warn(e){console.log(`${v.default.yellow("\u26A0")} ${e}`)},error(e){console.log(`${v.default.red("\u2716")} ${e}`)},completion(e,t){let o=t==="npm"?"npm run":t;console.log(""),console.log(v.default.green(v.default.bold(`\u{1F389} ${e} is ready!`))),console.log(""),console.log(v.default.bold("Next steps:")),console.log(v.default.cyan(` cd ${e}`)),console.log(v.default.cyan(` ${t} start`)),console.log(""),console.log(v.default.bold("Android:")),console.log(v.default.cyan(` ${o} android`)),console.log(""),console.log(v.default.bold("iOS:")),console.log(v.default.cyan(` ${o} ios`)),console.log(""),console.log(v.default.bold("Read details:")),console.log(v.default.cyan(" README.md")),console.log("")}};function H(e){if(!e||e.trim()==="")return{expoSdk:"~57.0.0",expoSdkTemplate:"blank@sdk-57"};let t=e.trim();if(t.toLowerCase()==="latest")return{expoSdk:"latest",expoSdkTemplate:"blank@latest"};if(/^\d{2}$/.test(t))return{expoSdk:`~${t}.0.0`,expoSdkTemplate:`blank@sdk-${t}`};let o=t.match(/(\d{2})\.\d+/);if(o){let n=o[1];return{expoSdk:t.startsWith("~")||t.startsWith("^")?t:`~${t}`,expoSdkTemplate:`blank@sdk-${n}`}}return{expoSdk:t,expoSdkTemplate:"blank@sdk-57"}}async function X(e){if(e)return H(e);let t=await(0,F.default)({type:"select",name:"sdk",message:"Select Expo SDK Version",choices:[{title:"SDK 57 (~57.0.0 - Recommended Default)",value:"~57.0.0"},{title:"SDK 58 (Latest)",value:"~58.0.0"},{title:"SDK 56 (~56.0.0)",value:"~56.0.0"},{title:"SDK 55 (~55.0.0)",value:"~55.0.0"},{title:"SDK 54 (~54.0.0)",value:"~54.0.0"},{title:"SDK 52 (~52.0.0 LTS)",value:"~52.0.0"},{title:"Custom / Manual Entry...",value:"manual"}],initial:0});if(!t.sdk)return H("~57.0.0");if(t.sdk==="manual"){let o=await(0,F.default)({type:"text",name:"version",message:"Enter Expo SDK version (e.g. 57, 58, 54, ~57.0.0, or latest):",initial:"57",validate:n=>n&&n.trim().length>0?!0:"SDK version cannot be empty"});return H(o.version||"57")}return H(t.sdk)}async function ue(e,t,o=process.cwd()){let n=!!t.yes,i=e;if(!i)if(n)i="MyApp";else{let y=await(0,F.default)({type:"text",name:"projectName",message:"Project name",initial:"MyApp",validate:g=>{let b=L(g);return b.valid?!0:b.error??"Invalid name"}});if(!y.projectName)return s.error("Operation cancelled."),null;i=y.projectName}if(!i)return s.error("Invalid project name."),null;let a=L(i);if(!a.valid)return s.error(a.error||"Invalid project name."),null;let c=a.sanitizedName,l=_(o,c);if(Y(l)&&!ce(l)){if(n)return s.error(`Directory "${c}" already exists and is not empty.`),s.warn("In --yes mode, existing directories are protected. Please choose another name or clear the directory."),null;let y=await(0,F.default)({type:"select",name:"dirAction",message:`Directory "${c}" already exists. What do you want to do?`,choices:[{title:"Cancel",value:"cancel"},{title:"Use another name",value:"rename"},{title:"Replace (overwrite existing directory)",value:"replace"}],initial:0});if(!y.dirAction||y.dirAction==="cancel")return s.error("Operation cancelled."),null;if(y.dirAction==="rename"){let g=await(0,F.default)({type:"text",name:"newName",message:"Enter new project name",initial:`${c}-app`,validate:E=>{let R=L(E);return R.valid?Y(_(o,E.trim()))?"Directory already exists!":!0:R.error??"Invalid name"}});if(!g.newName)return s.error("Operation cancelled."),null;let b=L(g.newName);l=_(o,b.sanitizedName)}else if(y.dirAction==="replace"){if(!(await(0,F.default)({type:"confirm",name:"value",message:`Are you sure you want to replace "${c}"? All files inside will be DELETED.`,initial:!1})).value)return s.error("Operation cancelled."),null;le(l)}}let p=t.pm??me();if(n){let y=H(t.expoSdk);return{projectName:c,targetDir:l,mode:"complete",platform:t.platform??"expo",typescript:!0,responsive:t.responsive??!0,theme:t.theme??!0,components:t.components??!0,navigation:t.navigation??!0,tabs:t.tabs??!0,drawer:t.drawer??!0,api:t.api??!0,query:t.query??!0,service:t.service??!0,auth:t.auth??!0,guest:t.guest??!0,storage:t.storage??!0,examples:t.examples??!0,husky:t.husky??!0,expoSdk:y.expoSdk,expoSdkTemplate:y.expoSdkTemplate,pm:p}}let u=await(0,F.default)({type:"select",name:"mode",message:"What type of project do you want?",choices:[{title:"Complete App (Recommended)",value:"complete"},{title:"Custom Setup",value:"custom"},{title:"Minimal App",value:"minimal"}],initial:0});if(!u.mode)return s.error("Operation cancelled."),null;let h=u.mode;if(h==="complete"){let g=(await(0,F.default)({type:"select",name:"platform",message:"Choose platform",choices:[{title:"Expo",value:"expo"},{title:"React Native CLI",value:"cli"}],initial:0})).platform||"expo",b={expoSdk:"~57.0.0",expoSdkTemplate:"blank@sdk-57"};return g==="expo"&&(b=await X(t.expoSdk)),{projectName:c,targetDir:l,mode:"complete",platform:g,typescript:!0,responsive:!0,theme:!0,components:!0,navigation:!0,tabs:!0,drawer:!0,api:!0,query:!0,service:!0,auth:!0,guest:!0,storage:!0,examples:!0,husky:!0,expoSdk:b.expoSdk,expoSdkTemplate:b.expoSdkTemplate,pm:p}}if(h==="minimal"){let g=(await(0,F.default)({type:"select",name:"platform",message:"Choose platform",choices:[{title:"Expo",value:"expo"},{title:"React Native CLI",value:"cli"}],initial:0})).platform||"expo",b={expoSdk:"~57.0.0",expoSdkTemplate:"blank@sdk-57"};return g==="expo"&&(b=await X(t.expoSdk)),{projectName:c,targetDir:l,mode:"minimal",platform:g,typescript:!0,responsive:t.responsive??!0,theme:t.theme??!0,components:t.components??!0,navigation:t.navigation??!0,tabs:!1,drawer:!1,api:t.api??!1,query:t.query??!1,service:t.service??!1,auth:t.auth??!1,guest:t.guest??!1,storage:t.storage??!1,examples:!1,husky:!1,expoSdk:b.expoSdk,expoSdkTemplate:b.expoSdkTemplate,pm:p}}let m=await(0,F.default)({type:"select",name:"platform",message:"Choose platform",choices:[{title:"Expo",value:"expo"},{title:"React Native CLI",value:"cli"}],initial:0});if(!m.platform)return s.error("Operation cancelled."),null;let w=m.platform,d={expoSdk:"~57.0.0",expoSdkTemplate:"blank@sdk-57"};w==="expo"&&(d=await X(t.expoSdk));let f=await(0,F.default)([{type:"confirm",name:"typescript",message:"TypeScript (Strict mode)?",initial:!0},{type:"confirm",name:"responsive",message:"Responsive (react-native-dynamic-responsive)?",initial:!0},{type:"confirm",name:"theme",message:"Theme Architecture (Light/Dark/System)?",initial:!0},{type:"confirm",name:"components",message:"Common UI Components (CText, CBox, CButton, etc.)?",initial:!0},{type:"confirm",name:"navigation",message:"Navigation Architecture?",initial:!0},{type:(y,g)=>g.navigation?"confirm":null,name:"tabs",message:"Bottom Tabs (5 Tabs Demo)?",initial:!0},{type:(y,g)=>g.navigation?"confirm":null,name:"drawer",message:"Do you want Drawer Navigation?",initial:!0},{type:"confirm",name:"api",message:"API Client Architecture?",initial:!0},{type:(y,g)=>g.api?"confirm":null,name:"query",message:"TanStack Query Setup?",initial:!0},{type:(y,g)=>g.api?"confirm":null,name:"service",message:"Service Architecture (Home, Profile, Auth)?",initial:!0},{type:"confirm",name:"auth",message:"Authentication Context & Provider?",initial:!0},{type:(y,g)=>g.auth?"confirm":null,name:"guest",message:"Guest Mode Support?",initial:!0},{type:"confirm",name:"storage",message:"Storage Abstraction Layer?",initial:!0},{type:"confirm",name:"husky",message:"Do you want error-free code / pre-commit hooks (Husky + Prettier + Lint-staged)?",initial:!0},{type:"confirm",name:"examples",message:"Example Screens & Demo Data?",initial:!0}]);return{projectName:c,targetDir:l,mode:"custom",platform:w,typescript:f.typescript??!0,responsive:f.responsive??!0,theme:f.theme??!0,components:f.components??!0,navigation:f.navigation??!0,tabs:f.tabs??!0,drawer:f.drawer??!0,api:f.api??!0,query:f.query??!0,service:f.service??!0,auth:f.auth??!0,guest:f.guest??!0,storage:f.storage??!0,examples:f.examples??!0,husky:f.husky??!0,expoSdk:d.expoSdk,expoSdkTemplate:d.expoSdkTemplate,pm:p}}var S=x(require("path")),V=x(require("fs"));function ge(e){let{targetDir:t,projectName:o,platform:n,navigation:i,tabs:a,drawer:c,query:l,storage:p,responsive:u,husky:h,components:m}=e,w=e.expoSdk||"~57.0.0",d={react:"19.2.3","react-native":"0.86.3","react-native-safe-area-context":"^5.0.0","react-native-screens":"^4.6.0"};n==="expo"&&(d.expo=w,d["expo-status-bar"]="~57.0.1",d["expo-asset"]="~57.0.2",d["react-dom"]="19.2.3",d["react-native-web"]="~0.19.13",d["@expo/metro-runtime"]="~4.0.1"),u&&(d["react-native-dynamic-responsive"]="^1.1.1"),l&&(d["@tanstack/react-query"]="^5.62.0"),i&&(d["@react-navigation/native"]="^7.0.0",d["@react-navigation/native-stack"]="^7.0.0",d["react-native-gesture-handler"]="~2.20.0",d["react-native-reanimated"]="~3.16.1",d["react-native-worklets"]="^0.12.1"),a&&(d["@react-navigation/bottom-tabs"]="^7.0.0"),c&&(d["@react-navigation/drawer"]="^7.0.0"),p&&(d["@react-native-async-storage/async-storage"]="^2.1.0");let f={"@babel/core":"^7.25.0","@babel/preset-env":"^7.25.0","@babel/runtime":"^7.25.0","@types/react":"~18.3.12","babel-plugin-module-resolver":"^5.0.0","create-mobile-app":"^1.0.0",typescript:"^5.5.0"};n==="expo"?f["babel-preset-expo"]=w:f["@react-native/babel-preset"]="^0.76.0",h&&(f.husky="^9.1.0",f["lint-staged"]="^15.2.0",f.prettier="^3.3.0");let y=n==="expo"?{start:"expo start",android:"expo start --android",ios:"expo start --ios",web:"expo start --web",lint:"tsc --noEmit",test:'echo "No test specified" && exit 0'}:{start:"react-native start",android:"react-native run-android",ios:"react-native run-ios",lint:"tsc --noEmit",test:'echo "No test specified" && exit 0'};h&&(y.prepare="husky",y.typecheck="tsc --noEmit",y.format='prettier --write "src/**/*.{ts,tsx,json,md}"');let g={name:o.toLowerCase(),version:"1.0.0",private:!0,scripts:y,dependencies:d,devDependencies:f,devminds:{generator:"create-mobile-app",website:"https://devminds.in",portfolio:"https://devminds.in/portfolio",author:"Dharmendra (Dev) kumar"}},b=S.default.join(t,"package.json");if(V.default.existsSync(b))try{let C=JSON.parse(V.default.readFileSync(b,"utf8")),J={...d,...C.dependencies};e.expoSdk&&e.expoSdk!=="latest"&&(J.expo=C.dependencies?.expo||e.expoSdk),g={...C,name:o.toLowerCase(),scripts:{...C.scripts,...y},dependencies:J,devDependencies:{...C.devDependencies,...f},devminds:{generator:"create-mobile-app",website:"https://devminds.in",portfolio:"https://devminds.in/portfolio",author:"Dharmendra (Dev) kumar"}}}catch{}if(h&&(g["lint-staged"]={"*.{ts,tsx}":["prettier --write"],"*.{json,md,js}":["prettier --write"]}),r(S.default.join(t,"package.json"),JSON.stringify(g,null,2)),h){let C={singleQuote:!0,trailingComma:"all",printWidth:100,tabWidth:2,semi:!0};r(S.default.join(t,".prettierrc"),JSON.stringify(C,null,2)),r(S.default.join(t,".prettierignore"),`node_modules/
5
+ dist/
6
+ .expo/
7
+ android/
8
+ ios/
9
+ `),r(S.default.join(t,".husky","pre-commit"),`npx tsc --noEmit
10
+ npx lint-staged
11
+ `)}let E={expo:{name:o,slug:o.toLowerCase().replace(/[^a-z0-9_-]/g,"-"),version:"1.0.0",orientation:"portrait",icon:"./assets/icon.png",userInterfaceStyle:"automatic",newArchEnabled:!0,splash:{image:"./assets/splash.png",resizeMode:"contain",backgroundColor:"#0015B6"},ios:{supportsTablet:!0},android:{adaptiveIcon:{foregroundImage:"./assets/adaptive-icon.png",backgroundColor:"#0015B6"}},web:{favicon:"./assets/favicon.png"}}},R=S.default.join(t,"app.json");if(V.default.existsSync(R))try{let C=JSON.parse(V.default.readFileSync(R,"utf8"));E={...C,expo:{...C.expo,name:o,slug:o.toLowerCase().replace(/[^a-z0-9_-]/g,"-"),icon:"./assets/icon.png",splash:{image:"./assets/splash.png",resizeMode:"contain",backgroundColor:"#0015B6",...C.expo?.splash||{}},android:{...C.expo?.android||{},adaptiveIcon:{foregroundImage:"./assets/adaptive-icon.png",backgroundColor:"#0015B6"}},web:{...C.expo?.web||{},favicon:"./assets/favicon.png"}}}}catch{}r(S.default.join(t,"app.json"),JSON.stringify(E,null,2));let W={compilerOptions:{target:"esnext",module:"commonjs",lib:["es2021"],allowJs:!0,jsx:"react-native",noEmit:!0,isolatedModules:!0,strict:!0,moduleResolution:"node",allowSyntheticDefaultImports:!0,esModuleInterop:!0,skipLibCheck:!0,baseUrl:".",paths:{"@/*":["src/*"]}},exclude:["node_modules","babel.config.js","metro.config.js"]};r(S.default.join(t,"tsconfig.json"),JSON.stringify(W,null,2));let Q=`module.exports = function (api) {
12
+ api.cache(true);
13
+ return {
14
+ presets: ['${n==="expo"?"babel-preset-expo":"module:@react-native/babel-preset"}'],
15
+ plugins: [
16
+ [
17
+ 'module-resolver',
18
+ {
19
+ root: ['./'],
20
+ alias: {
21
+ '@': './src',
22
+ },
23
+ },
24
+ ],
25
+ ${i?"'react-native-reanimated/plugin',":""}
26
+ ],
27
+ };
28
+ };
29
+ `;r(S.default.join(t,"babel.config.js"),Q);let q=n==="expo"?`const { getDefaultConfig } = require('expo/metro-config');
30
+ const config = getDefaultConfig(__dirname);
31
+ module.exports = config;
32
+ `:`const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
33
+ const config = {};
34
+ module.exports = mergeConfig(getDefaultConfig(__dirname), config);
35
+ `;r(S.default.join(t,".metro.config.js"),q),r(S.default.join(t,"metro.config.js"),q),r(S.default.join(t,".env.example"),`API_BASE_URL=https://api.example.com
36
+ APP_ENV=development
37
+ `),r(S.default.join(t,".gitignore"),`# node_modules
38
+ node_modules/
39
+ .expo/
40
+ dist/
41
+ npm-debug.log*
42
+ yarn-debug.log*
43
+ yarn-error.log*
44
+
45
+ # OS
46
+ .DS_Store
47
+ Thumbs.db
48
+
49
+ # Xcode & Android
50
+ *.xcworkspace
51
+ *.xcodeproj
52
+ android/app/build
53
+ android/build
54
+ ios/Pods
55
+ ios/build
56
+
57
+ # Environment
58
+ .env
59
+ .env.local
60
+ `);let G=`/**
61
+ * Project: ${o}
62
+ * Generated with create-mobile-app by DevMinds Technologies
63
+ *
64
+ * DevMinds Technologies (WEB \xB7 MOBILE \xB7 CLOUD \xB7 DESIGN)
65
+ * Website: https://devminds.in
66
+ * Portfolio: https://devminds.in/portfolio
67
+ * Lead Developer: Dharmendra (Dev) kumar (https://www.linkedin.com/in/dharmendra-appdeveloper)
68
+ */
69
+
70
+ import React from 'react';
71
+ import { SafeAreaProvider } from 'react-native-safe-area-context';
72
+ ${e.theme?"import { AppTheme } from '@/theme';":""}
73
+ ${e.query?`import { QueryClientProvider } from '@tanstack/react-query';
74
+ import { queryClient } from '@/api';`:""}
75
+ ${m?"import { AlertProvider } from '@/components';":""}
76
+ ${e.navigation?`import { NavigationContainer } from '@react-navigation/native';
77
+ import { RootNavigator } from '@/navigations';`:""}
78
+ ${e.auth?"import { AuthProvider } from '@/auth';":""}
79
+ ${e.navigation?"":"import { StyleSheet, Text, View } from 'react-native';"}
80
+
81
+ export default function App() {
82
+ return (
83
+ <SafeAreaProvider>
84
+ ${e.theme?"<AppTheme>":"<>"}${e.query?`
85
+ <QueryClientProvider client={queryClient}>`:""}${m?`
86
+ <AlertProvider>`:""}${e.auth?`
87
+ <AuthProvider>`:""}${e.navigation?`
88
+ <NavigationContainer>
89
+ <RootNavigator />
90
+ </NavigationContainer>`:`
91
+ <View style={styles.container}>
92
+ <Text style={styles.title}>Welcome to ${o}</Text>
93
+ <Text style={styles.subtitle}>Architecture set up successfully.</Text>
94
+ </View>`}${e.auth?`
95
+ </AuthProvider>`:""}${m?`
96
+ </AlertProvider>`:""}${e.query?`
97
+ </QueryClientProvider>`:""}${e.theme?`
98
+ </AppTheme>`:`
99
+ </>`}
100
+ </SafeAreaProvider>
101
+ );
102
+ }
103
+
104
+ ${e.navigation?"":`const styles = StyleSheet.create({
105
+ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F8FAFC' },
106
+ title: { fontSize: 24, fontWeight: 'bold', color: '#0F172A', marginBottom: 8 },
107
+ subtitle: { fontSize: 16, color: '#64748B' },
108
+ });`}
109
+ `;r(S.default.join(t,"App.tsx"),G)}var I=x(require("path"));function fe(e){if(!e.api)return;let{targetDir:t,query:o}=e,n=I.default.join(t,"src","api");r(I.default.join(n,"api.ts"),`import { env } from '@/config';
110
+ import { handleApiError, normalizeResponse } from './handle';
111
+
112
+ export interface RequestConfig extends RequestInit {
113
+ token?: string;
114
+ params?: Record<string, string | number | boolean>;
115
+ }
116
+
117
+ export interface ApiResponse<T = any> {
118
+ data: T;
119
+ status: number;
120
+ message?: string;
121
+ ok: boolean;
122
+ }
123
+
124
+ class ApiClient {
125
+ private baseUrl: string;
126
+ private defaultHeaders: Record<string, string>;
127
+
128
+ constructor(baseUrl: string = env.API_BASE_URL) {
129
+ this.baseUrl = baseUrl;
130
+ this.defaultHeaders = {
131
+ 'Content-Type': 'application/json',
132
+ Accept: 'application/json',
133
+ };
134
+ }
135
+
136
+ private buildUrl(endpoint: string, params?: Record<string, string | number | boolean>): string {
137
+ const cleanEndpoint = endpoint.startsWith('/') ? endpoint : \`/\${endpoint}\`;
138
+ const fullUrl = this.baseUrl.endsWith('/')
139
+ ? \`\${this.baseUrl.slice(0, -1)}\${cleanEndpoint}\`
140
+ : \`\${this.baseUrl}\${cleanEndpoint}\`;
141
+
142
+ if (!params || Object.keys(params).length === 0) {
143
+ return fullUrl;
144
+ }
145
+
146
+ const searchParams = new URLSearchParams();
147
+ Object.entries(params).forEach(([key, val]) => {
148
+ if (val !== undefined && val !== null) {
149
+ searchParams.append(key, String(val));
150
+ }
151
+ });
152
+
153
+ return \`\${fullUrl}?\${searchParams.toString()}\`;
154
+ }
155
+
156
+ private async request<T>(endpoint: string, config: RequestConfig = {}): Promise<ApiResponse<T>> {
157
+ const { token, params, headers, ...customConfig } = config;
158
+ const url = this.buildUrl(endpoint, params);
159
+
160
+ const requestHeaders: Record<string, string> = {
161
+ ...this.defaultHeaders,
162
+ ...(headers as Record<string, string>),
163
+ };
164
+
165
+ if (token) {
166
+ requestHeaders['Authorization'] = \`Bearer \${token}\`;
167
+ }
168
+
169
+ try {
170
+ const response = await fetch(url, {
171
+ ...customConfig,
172
+ headers: requestHeaders,
173
+ });
174
+
175
+ return await normalizeResponse<T>(response);
176
+ } catch (error) {
177
+ throw handleApiError(error);
178
+ }
179
+ }
180
+
181
+ public async get<T>(endpoint: string, config?: RequestConfig): Promise<ApiResponse<T>> {
182
+ return this.request<T>(endpoint, { ...config, method: 'GET' });
183
+ }
184
+
185
+ public async post<T>(endpoint: string, body?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
186
+ return this.request<T>(endpoint, {
187
+ ...config,
188
+ method: 'POST',
189
+ body: body ? JSON.stringify(body) : undefined,
190
+ });
191
+ }
192
+
193
+ public async put<T>(endpoint: string, body?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
194
+ return this.request<T>(endpoint, {
195
+ ...config,
196
+ method: 'PUT',
197
+ body: body ? JSON.stringify(body) : undefined,
198
+ });
199
+ }
200
+
201
+ public async patch<T>(endpoint: string, body?: any, config?: RequestConfig): Promise<ApiResponse<T>> {
202
+ return this.request<T>(endpoint, {
203
+ ...config,
204
+ method: 'PATCH',
205
+ body: body ? JSON.stringify(body) : undefined,
206
+ });
207
+ }
208
+
209
+ public async delete<T>(endpoint: string, config?: RequestConfig): Promise<ApiResponse<T>> {
210
+ return this.request<T>(endpoint, { ...config, method: 'DELETE' });
211
+ }
212
+ }
213
+
214
+ export const apiClient = new ApiClient();
215
+ `),r(I.default.join(n,"handle","index.ts"),`import { ApiResponse } from '../api';
216
+
217
+ export interface NormalizedError {
218
+ message: string;
219
+ status?: number;
220
+ code?: string;
221
+ raw?: any;
222
+ }
223
+
224
+ export async function normalizeResponse<T>(response: Response): Promise<ApiResponse<T>> {
225
+ let data: any;
226
+
227
+ try {
228
+ const text = await response.text();
229
+ data = text ? JSON.parse(text) : {};
230
+ } catch {
231
+ data = {};
232
+ }
233
+
234
+ if (!response.ok) {
235
+ const error: NormalizedError = {
236
+ message: data?.message || data?.error || \`HTTP error \${response.status}\`,
237
+ status: response.status,
238
+ code: data?.code,
239
+ raw: data,
240
+ };
241
+ throw error;
242
+ }
243
+
244
+ return {
245
+ data: data as T,
246
+ status: response.status,
247
+ message: data?.message,
248
+ ok: response.ok,
249
+ };
250
+ }
251
+
252
+ export function handleApiError(error: any): NormalizedError {
253
+ if (error && typeof error === 'object' && 'message' in error) {
254
+ return {
255
+ message: error.message || 'An unexpected error occurred.',
256
+ status: error.status,
257
+ code: error.code,
258
+ raw: error,
259
+ };
260
+ }
261
+
262
+ return {
263
+ message: typeof error === 'string' ? error : 'Network error or service unavailable.',
264
+ raw: error,
265
+ };
266
+ }
267
+ `),r(I.default.join(n,"handle","queryKey","index.ts"),`export const queryKeys = {
268
+ home: {
269
+ all: ['home'] as const,
270
+ detail: (id: string) => ['home', id] as const,
271
+ },
272
+ profile: {
273
+ all: ['profile'] as const,
274
+ },
275
+ auth: {
276
+ user: ['auth', 'user'] as const,
277
+ },
278
+ search: {
279
+ results: (query: string) => ['search', query] as const,
280
+ },
281
+ notification: {
282
+ all: ['notification'] as const,
283
+ },
284
+ } as const;
285
+ `),o&&r(I.default.join(n,"queryClient.ts"),`import { QueryClient } from '@tanstack/react-query';
286
+
287
+ export const queryClient = new QueryClient({
288
+ defaultOptions: {
289
+ queries: {
290
+ retry: 1,
291
+ staleTime: 1000 * 60 * 5, // 5 minutes
292
+ gcTime: 1000 * 60 * 30, // 30 minutes
293
+ refetchOnWindowFocus: false,
294
+ },
295
+ mutations: {
296
+ retry: 0,
297
+ },
298
+ },
299
+ });
300
+ `);let l=`export * from './api';
301
+ export * from './handle';
302
+ export * from './handle/queryKey';
303
+ ${o?"export * from './queryClient';":""}
304
+ `;r(I.default.join(n,"index.ts"),l)}var k=x(require("path"));function ye(e){if(!e.service)return;let{targetDir:t,query:o,api:n}=e,i=k.default.join(t,"src","service"),a=n?"import { apiClient } from '@/api';":"",c=o?`import { useQuery, useMutation } from '@tanstack/react-query';
305
+ import { queryKeys } from '@/api';`:"",l=`${a}
306
+
307
+ export interface HomeData {
308
+ welcomeMessage: string;
309
+ featuredItems: { id: string; title: string; subtitle: string }[];
310
+ todayOverview: { activeUsers: number; totalSales: number };
311
+ }
312
+
313
+ export const Home = {
314
+ getHome: async (): Promise<HomeData> => {
315
+ ${n?`const response = await apiClient.get<HomeData>('/home');
316
+ return response.data;`:`// Mock implementation
317
+ return {
318
+ welcomeMessage: 'Welcome to your application architecture!',
319
+ featuredItems: [
320
+ { id: '1', title: 'Modular Architecture', subtitle: 'Senior-level, scalable React Native structure' },
321
+ { id: '2', title: 'Dynamic Responsive', subtitle: 'Powered by react-native-dynamic-responsive' },
322
+ { id: '3', title: 'TanStack Query', subtitle: 'Server state & cache management setup' },
323
+ ],
324
+ todayOverview: { activeUsers: 1420, totalSales: 8900 },
325
+ };`}
326
+ },
327
+
328
+ getDetails: async (id: string) => {
329
+ ${n?"const response = await apiClient.get(`/home/${id}`);\n return response.data;":"return { id, title: `Item ${id}`, description: 'Detailed information for item' };"}
330
+ },
331
+ };
332
+ `;if(r(k.default.join(i,"home","index.ts"),l),o){let m=`${c}
333
+ import { Home, HomeData } from './index';
334
+
335
+ export const useHomeQuery = () => {
336
+ return useQuery<HomeData>({
337
+ queryKey: queryKeys.home.all,
338
+ queryFn: Home.getHome,
339
+ });
340
+ };
341
+
342
+ export const useHomeDetailsQuery = (id: string) => {
343
+ return useQuery({
344
+ queryKey: queryKeys.home.detail(id),
345
+ queryFn: () => Home.getDetails(id),
346
+ enabled: Boolean(id),
347
+ });
348
+ };
349
+ `;r(k.default.join(i,"home","query.ts"),m)}let p=`${a}
350
+
351
+ export interface UserProfile {
352
+ id: string;
353
+ name: string;
354
+ email: string;
355
+ phone: string;
356
+ avatarUrl?: string;
357
+ role: string;
358
+ }
359
+
360
+ export const Profile = {
361
+ getProfile: async (): Promise<UserProfile> => {
362
+ ${n?`const response = await apiClient.get<UserProfile>('/profile');
363
+ return response.data;`:`return {
364
+ id: 'usr_1001',
365
+ name: 'John Doe',
366
+ email: 'john.doe@example.com',
367
+ phone: '+1 555-0199',
368
+ role: 'Senior Lead Architect',
369
+ };`}
370
+ },
371
+
372
+ updateProfile: async (data: Partial<UserProfile>): Promise<UserProfile> => {
373
+ ${n?`const response = await apiClient.patch<UserProfile>('/profile', data);
374
+ return response.data;`:`return {
375
+ id: 'usr_1001',
376
+ name: data.name || 'John Doe',
377
+ email: data.email || 'john.doe@example.com',
378
+ phone: data.phone || '+1 555-0199',
379
+ role: 'Senior Lead Architect',
380
+ };`}
381
+ },
382
+ };
383
+ `;if(r(k.default.join(i,"profile","index.ts"),p),o){let m=`${c}
384
+ import { Profile, UserProfile } from './index';
385
+
386
+ export const useProfileQuery = () => {
387
+ return useQuery<UserProfile>({
388
+ queryKey: queryKeys.profile.all,
389
+ queryFn: Profile.getProfile,
390
+ });
391
+ };
392
+
393
+ export const useUpdateProfileMutation = () => {
394
+ return useMutation({
395
+ mutationFn: (data: Partial<UserProfile>) => Profile.updateProfile(data),
396
+ });
397
+ };
398
+ `;r(k.default.join(i,"profile","query.ts"),m)}let u=`${a}
399
+
400
+ export interface LoginParams {
401
+ email: string;
402
+ password?: string;
403
+ }
404
+
405
+ export interface AuthResponse {
406
+ token: string;
407
+ user: {
408
+ id: string;
409
+ name: string;
410
+ email: string;
411
+ };
412
+ }
413
+
414
+ export const Auth = {
415
+ login: async (params: LoginParams): Promise<AuthResponse> => {
416
+ ${n?`const response = await apiClient.post<AuthResponse>('/auth/login', params);
417
+ return response.data;`:`return {
418
+ token: 'demo-bearer-token-xyz-123',
419
+ user: {
420
+ id: 'usr_1001',
421
+ name: 'John Doe',
422
+ email: params.email,
423
+ },
424
+ };`}
425
+ },
426
+
427
+ logout: async (): Promise<void> => {
428
+ ${n?"await apiClient.post('/auth/logout');":`// Mock logout request
429
+ return Promise.resolve();`}
430
+ },
431
+
432
+ me: async () => {
433
+ ${n?`const response = await apiClient.get('/auth/me');
434
+ return response.data;`:"return { id: 'usr_1001', name: 'John Doe', email: 'john.doe@example.com' };"}
435
+ },
436
+ };
437
+ `;if(r(k.default.join(i,"auth","index.ts"),u),o){let m=`${c}
438
+ import { Auth, LoginParams } from './index';
439
+
440
+ export const useLoginMutation = () => {
441
+ return useMutation({
442
+ mutationFn: (params: LoginParams) => Auth.login(params),
443
+ });
444
+ };
445
+
446
+ export const useLogoutMutation = () => {
447
+ return useMutation({
448
+ mutationFn: () => Auth.logout(),
449
+ });
450
+ };
451
+
452
+ export const useMeQuery = () => {
453
+ return useQuery({
454
+ queryKey: queryKeys.auth.user,
455
+ queryFn: Auth.me,
456
+ });
457
+ };
458
+ `;r(k.default.join(i,"auth","query.ts"),m)}let h=`${a}
459
+
460
+ export const User = {
461
+ getUsers: async () => {
462
+ ${n?`const response = await apiClient.get('/users');
463
+ return response.data;`:`return [
464
+ { id: '1', name: 'Alice Smith', email: 'alice@example.com' },
465
+ { id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
466
+ ];`}
467
+ },
468
+ };
469
+ `;if(r(k.default.join(i,"user","index.ts"),h),o){let m=`${c}
470
+ import { User } from './index';
471
+
472
+ export const useUsersQuery = () => {
473
+ return useQuery({
474
+ queryKey: ['users'],
475
+ queryFn: User.getUsers,
476
+ });
477
+ };
478
+ `;r(k.default.join(i,"user","query.ts"),m)}}var $=x(require("path"));function xe(e){if(!e.navigation)return;let{targetDir:t,tabs:o,drawer:n,auth:i}=e,a=$.default.join(t,"src","navigations");if(r($.default.join(a,"screenName","index.ts"),`export const ScreenName = {
479
+ Home: 'Home',
480
+ Search: 'Search',
481
+ Notification: 'Notification',
482
+ Profile: 'Profile',
483
+ Settings: 'Settings',
484
+ Login: 'Login',
485
+ MainTab: 'MainTab',
486
+ MainDrawer: 'MainDrawer',
487
+ } as const;
488
+
489
+ export type ScreenNameKey = keyof typeof ScreenName;
490
+ `),r($.default.join(a,"types.ts"),`import { ScreenName } from './screenName';
491
+
492
+ export type RootStackParamList = {
493
+ [ScreenName.Login]: undefined;
494
+ [ScreenName.MainTab]?: undefined;
495
+ [ScreenName.MainDrawer]?: undefined;
496
+ [ScreenName.Home]: undefined;
497
+ [ScreenName.Search]: undefined;
498
+ [ScreenName.Notification]: undefined;
499
+ [ScreenName.Profile]: undefined;
500
+ [ScreenName.Settings]: undefined;
501
+ };
502
+ `),o&&r($.default.join(a,"Bottom","index.tsx"),`import React from 'react';
503
+ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
504
+ import { ScreenName } from '../screenName';
505
+ import { HomeScreen } from '@/screen/Home';
506
+ import { SearchScreen } from '@/screen/Search';
507
+ import { NotificationScreen } from '@/screen/Notification';
508
+ import { ProfileScreen } from '@/screen/Profile';
509
+ import { SettingsScreen } from '@/screen/Settings';
510
+ import { Text } from 'react-native';
511
+
512
+ const Tab = createBottomTabNavigator();
513
+
514
+ export function BottomTabNavigator() {
515
+ return (
516
+ <Tab.Navigator
517
+ screenOptions={({ route }) => ({
518
+ headerShown: true,
519
+ tabBarActiveTintColor: '#0284C7',
520
+ tabBarInactiveTintColor: '#64748B',
521
+ tabBarStyle: {
522
+ backgroundColor: '#FFFFFF',
523
+ borderTopColor: '#E2E8F0',
524
+ },
525
+ tabBarIcon: ({ color, size }) => {
526
+ let icon = '\u{1F4F1}';
527
+ if (route.name === ScreenName.Home) icon = '\u{1F3E0}';
528
+ if (route.name === ScreenName.Search) icon = '\u{1F50D}';
529
+ if (route.name === ScreenName.Notification) icon = '\u{1F514}';
530
+ if (route.name === ScreenName.Profile) icon = '\u{1F464}';
531
+ if (route.name === ScreenName.Settings) icon = '\u2699\uFE0F';
532
+ return <Text style={{ fontSize: size - 4 }}>{icon}</Text>;
533
+ },
534
+ })}
535
+ >
536
+ <Tab.Screen name={ScreenName.Home} component={HomeScreen} />
537
+ <Tab.Screen name={ScreenName.Search} component={SearchScreen} />
538
+ <Tab.Screen name={ScreenName.Notification} component={NotificationScreen} />
539
+ <Tab.Screen name={ScreenName.Profile} component={ProfileScreen} />
540
+ <Tab.Screen name={ScreenName.Settings} component={SettingsScreen} />
541
+ </Tab.Navigator>
542
+ );
543
+ }
544
+ `),n){let h=`import React from 'react';
545
+ import { createDrawerNavigator } from '@react-navigation/drawer';
546
+ import { ScreenName } from '../screenName';
547
+ import { HomeScreen } from '@/screen/Home';
548
+ import { ProfileScreen } from '@/screen/Profile';
549
+ import { SettingsScreen } from '@/screen/Settings';
550
+ ${o?"import { BottomTabNavigator } from '../Bottom';":""}
551
+
552
+ const Drawer = createDrawerNavigator();
553
+
554
+ export function DrawerNavigator() {
555
+ return (
556
+ <Drawer.Navigator
557
+ screenOptions={{
558
+ headerShown: true,
559
+ drawerActiveTintColor: '#0284C7',
560
+ drawerInactiveTintColor: '#64748B',
561
+ }}
562
+ >
563
+ ${o?`<Drawer.Screen
564
+ name={ScreenName.MainTab}
565
+ component={BottomTabNavigator}
566
+ options={{ title: 'Main Hub' }}
567
+ />`:""}
568
+ <Drawer.Screen name={ScreenName.Home} component={HomeScreen} />
569
+ <Drawer.Screen name={ScreenName.Profile} component={ProfileScreen} />
570
+ <Drawer.Screen name={ScreenName.Settings} component={SettingsScreen} />
571
+ </Drawer.Navigator>
572
+ );
573
+ }
574
+ `;r($.default.join(a,"Drawer","index.tsx"),h)}let p=`import React from 'react';
575
+ import { createNativeStackNavigator } from '@react-navigation/native-stack';
576
+ import { ScreenName } from '../screenName';
577
+ import { LoginScreen } from '@/screen/Login';
578
+ import { HomeScreen } from '@/screen/Home';
579
+ ${o?"import { BottomTabNavigator } from '../Bottom';":""}
580
+ ${n?"import { DrawerNavigator } from '../Drawer';":""}
581
+
582
+ const Stack = createNativeStackNavigator();
583
+
584
+ export function AuthNavigator() {
585
+ return (
586
+ <Stack.Navigator screenOptions={{ headerShown: false }}>
587
+ <Stack.Screen name={ScreenName.Login} component={LoginScreen} />
588
+ </Stack.Navigator>
589
+ );
590
+ }
591
+
592
+ export function AppNavigator() {
593
+ return (
594
+ <Stack.Navigator screenOptions={{ headerShown: false }}>
595
+ ${n?"<Stack.Screen name={ScreenName.MainDrawer} component={DrawerNavigator} />":o?"<Stack.Screen name={ScreenName.MainTab} component={BottomTabNavigator} />":"<Stack.Screen name={ScreenName.Home} component={HomeScreen} />"}
596
+ </Stack.Navigator>
597
+ );
598
+ }
599
+ `;r($.default.join(a,"Stack","index.tsx"),p);let u=`import React from 'react';
600
+ ${i?"import { useAuth } from '@/auth';":""}
601
+ import { AuthNavigator, AppNavigator } from './Stack';
602
+ ${i?"import { CLoading } from '@/components';":""}
603
+
604
+ export function RootNavigator() {
605
+ ${i?`const { isAuthenticated, isGuest, isLoading } = useAuth();
606
+
607
+ if (isLoading) {
608
+ return <CLoading message="Restoring session..." />;
609
+ }
610
+
611
+ if (!isAuthenticated && !isGuest) {
612
+ return <AuthNavigator />;
613
+ }`:""}
614
+
615
+ return <AppNavigator />;
616
+ }
617
+
618
+ export * from './screenName';
619
+ export * from './types';
620
+ `;r($.default.join(a,"index.tsx"),u)}var T=x(require("path"));function he(e){let{targetDir:t,responsive:o,theme:n,components:i,query:a,service:c,auth:l}=e,p=T.default.join(t,"src","screen"),u=o?"import { useStyles, useResponsive } from 'react-native-dynamic-responsive';":"",h=n?"import { useTheme } from '@/theme';":"",m=i?"import { CBox, CText, CButton, CInput, CDropdown, CLoading, CError, ShowAlert } from '@/components';":"",w=a&&c?`import { useHomeQuery } from '@/service/home/query';
621
+ import { useProfileQuery, useUpdateProfileMutation } from '@/service/profile/query';`:"",d=l?"import { useAuth } from '@/auth';":"",f=`import React from 'react';
622
+ import { ScrollView, Linking, TouchableOpacity, StyleSheet } from 'react-native';
623
+ ${u}
624
+ ${h}
625
+ ${m}
626
+ ${w}
627
+ import { styles } from './styles';
628
+
629
+ export function HomeScreen() {
630
+ ${o?"const r = useResponsive();":""}
631
+ ${n?"const { theme } = useTheme();":""}
632
+ ${a&&c?"const { data: homeData, isLoading, isError, refetch } = useHomeQuery();":""}
633
+
634
+ ${a&&c?`if (isLoading) return <CLoading message="Loading dashboard..." />;
635
+ if (isError) return <CError message="Failed to load home overview." onRetry={refetch} />;`:""}
636
+
637
+ const openLinkedIn = () => {
638
+ Linking.openURL('https://www.linkedin.com/in/dharmendra-appdeveloper');
639
+ };
640
+
641
+ return (
642
+ <ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
643
+ <CBox padding="md" gap="md">
644
+ ${o?`{/* Device Information powered by react-native-dynamic-responsive */}
645
+ <CBox row align="center" justify="space-between">
646
+ <CText variant="caption" color="#0284C7" style={{ fontWeight: '600' }}>
647
+ \u{1F4F1} Device: {r.isTablet ? 'Tablet' : r.isFold ? 'Foldable' : 'Phone'} ({Math.round(r.width)}x{Math.round(r.height)})
648
+ </CText>
649
+ <CText variant="caption" color="#64748B">
650
+ {r.isLandscape ? 'Landscape \u{1F504}' : 'Portrait \u{1F4F1}'}
651
+ </CText>
652
+ </CBox>`:""}
653
+
654
+ {/* Company Hero Card */}
655
+ <CBox style={styles.heroCard}>
656
+ <CBox style={styles.companyBadge}>
657
+ <CText variant="caption" color="#FFFFFF" style={{ fontWeight: 'bold' }}>
658
+ DevMinds Technology
659
+ </CText>
660
+ </CBox>
661
+ <CText variant="header" color="#FFFFFF">
662
+ Production App Foundation
663
+ </CText>
664
+ <CText variant="body" color="#94A3B8">
665
+ ${a&&c?"{homeData?.welcomeMessage}":"Senior-level React Native & TypeScript Mobile Architecture."}
666
+ </CText>
667
+ </CBox>
668
+
669
+ ${o?`{/* Responsive Package Scaling Showcase */}
670
+ <CBox style={styles.responsiveCard}>
671
+ <CText variant="subtitle" color="#0284C7">\u{1F4D0} react-native-dynamic-responsive</CText>
672
+ <CText variant="body" color="#475569">
673
+ Scale: {r.s(16).toFixed(1)}px | Vertical: {r.vs(16).toFixed(1)}px | Moderate: {r.ms(16).toFixed(1)}px
674
+ </CText>
675
+ </CBox>`:""}
676
+
677
+ {/* Feature Highlights */}
678
+ <CBox gap="md">
679
+ <CText variant="title">Architecture Highlights</CText>
680
+
681
+ <CBox style={styles.card}>
682
+ <CText variant="subtitle">\u26A1 High Performance</CText>
683
+ <CText variant="body" color="#64748B">
684
+ Server state powered by TanStack Query & dynamic responsive layout system.
685
+ </CText>
686
+ </CBox>
687
+
688
+ <CBox style={styles.card}>
689
+ <CText variant="subtitle">\u{1F3A8} Design & Theme System</CText>
690
+ <CText variant="body" color="#64748B">
691
+ Light/Dark/System theme context and responsive components (CText, CBox, CButton, ShowAlert).
692
+ </CText>
693
+ </CBox>
694
+ </CBox>
695
+
696
+ {/* Developer Profile Card */}
697
+ <CBox style={styles.card}>
698
+ <CText variant="subtitle">\u{1F464} Created by Dharmendra</CText>
699
+ <CText variant="body" color="#64748B">
700
+ Senior Mobile Architect & Developer at DevMinds Technology. Connect on LinkedIn for custom architecture & package developments.
701
+ </CText>
702
+ <TouchableOpacity style={styles.profileBtn} activeOpacity={0.8} onPress={openLinkedIn}>
703
+ <CText variant="button" color="#FFFFFF" style={{ fontWeight: '600' }}>
704
+ Connect on LinkedIn
705
+ </CText>
706
+ </TouchableOpacity>
707
+ </CBox>
708
+ </CBox>
709
+ </ScrollView>
710
+ );
711
+ }
712
+ `;r(T.default.join(p,"Home","index.tsx"),f);let y=`${u}
713
+
714
+ export const styles = ${o?`useStyles((r) => ({
715
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
716
+ contentContainer: { paddingVertical: r.vs(16) },
717
+ heroCard: {
718
+ backgroundColor: '#0F172A',
719
+ padding: r.ms(20),
720
+ borderRadius: r.ms(16),
721
+ gap: r.vs(8),
722
+ },
723
+ companyBadge: {
724
+ backgroundColor: '#0284C7',
725
+ alignSelf: 'flex-start',
726
+ paddingHorizontal: r.s(10),
727
+ paddingVertical: r.vs(4),
728
+ borderRadius: r.ms(20),
729
+ },
730
+ responsiveCard: {
731
+ backgroundColor: '#E0F2FE',
732
+ padding: r.ms(14),
733
+ borderRadius: r.ms(12),
734
+ borderWidth: 1,
735
+ borderColor: '#BAE6FD',
736
+ gap: r.vs(4),
737
+ },
738
+ card: {
739
+ backgroundColor: '#FFFFFF',
740
+ padding: r.ms(16),
741
+ borderRadius: r.ms(12),
742
+ borderWidth: 1,
743
+ borderColor: '#E2E8F0',
744
+ gap: r.vs(4),
745
+ },
746
+ profileBtn: {
747
+ backgroundColor: '#0284C7',
748
+ borderRadius: r.ms(10),
749
+ paddingVertical: r.vs(12),
750
+ paddingHorizontal: r.s(16),
751
+ alignItems: 'center',
752
+ marginTop: r.vs(8),
753
+ },
754
+ }));`:`StyleSheet.create({
755
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
756
+ contentContainer: { paddingVertical: 16 },
757
+ heroCard: { backgroundColor: '#0F172A', padding: 20, borderRadius: 16, gap: 8 },
758
+ companyBadge: { backgroundColor: '#0284C7', alignSelf: 'flex-start', paddingHorizontal: 10, paddingVertical: 4, borderRadius: 20 },
759
+ responsiveCard: { backgroundColor: '#E0F2FE', padding: 14, borderRadius: 12, borderWidth: 1, borderColor: '#BAE6FD', gap: 4 },
760
+ card: { backgroundColor: '#FFFFFF', padding: 16, borderRadius: 12, borderWidth: 1, borderColor: '#E2E8F0', gap: 4 },
761
+ profileBtn: { backgroundColor: '#0284C7', borderRadius: 10, paddingVertical: 12, paddingHorizontal: 16, alignItems: 'center', marginTop: 8 },
762
+ });`}
763
+ `;r(T.default.join(p,"Home","styles.ts"),y);let g=`import React, { useState } from 'react';
764
+ import { View } from 'react-native';
765
+ ${u}
766
+ ${m}
767
+ import { styles } from './styles';
768
+
769
+ export function SearchScreen() {
770
+ const [query, setQuery] = useState('');
771
+
772
+ return (
773
+ <View style={styles.container}>
774
+ <CBox padding="md" gap="md">
775
+ <CText variant="header">Search Catalogue</CText>
776
+ <CInput
777
+ label="Search"
778
+ placeholder="Type to search modules..."
779
+ value={query}
780
+ onChangeText={setQuery}
781
+ />
782
+ {query.length > 0 ? (
783
+ <CText variant="caption" color="#64748B">
784
+ Showing results for "{query}"
785
+ </CText>
786
+ ) : null}
787
+ </CBox>
788
+ </View>
789
+ );
790
+ }
791
+ `;r(T.default.join(p,"Search","index.tsx"),g);let b=`${u}
792
+
793
+ export const styles = ${o?`useStyles((r) => ({
794
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
795
+ }));`:`StyleSheet.create({
796
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
797
+ });`}
798
+ `;r(T.default.join(p,"Search","styles.ts"),b);let E=`import React from 'react';
799
+ import { ScrollView } from 'react-native';
800
+ ${u}
801
+ ${m}
802
+ import { styles } from './styles';
803
+
804
+ export function NotificationScreen() {
805
+ const notifications = [
806
+ { id: '1', title: 'DevMinds Technology', time: 'Just now', body: 'Architecture setup completed successfully.' },
807
+ { id: '2', title: 'Session Restored', time: '1h ago', body: 'Authentication state restored from local storage.' },
808
+ ];
809
+
810
+ return (
811
+ <ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
812
+ <CBox padding="md" gap="md">
813
+ <CText variant="header">Notifications</CText>
814
+ {notifications.map((item) => (
815
+ <CBox key={item.id} style={styles.itemCard}>
816
+ <CText variant="subtitle">{item.title}</CText>
817
+ <CText variant="body" color="#64748B">{item.body}</CText>
818
+ <CText variant="caption" color="#94A3B8">{item.time}</CText>
819
+ </CBox>
820
+ ))}
821
+ </CBox>
822
+ </ScrollView>
823
+ );
824
+ }
825
+ `;r(T.default.join(p,"Notification","index.tsx"),E);let R=`${u}
826
+
827
+ export const styles = ${o?`useStyles((r) => ({
828
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
829
+ contentContainer: { paddingVertical: r.vs(16) },
830
+ itemCard: {
831
+ backgroundColor: '#FFFFFF',
832
+ padding: r.ms(16),
833
+ borderRadius: r.ms(12),
834
+ borderWidth: 1,
835
+ borderColor: '#E2E8F0',
836
+ gap: r.vs(4),
837
+ },
838
+ }));`:`StyleSheet.create({
839
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
840
+ contentContainer: { paddingVertical: 16 },
841
+ itemCard: {
842
+ backgroundColor: '#FFFFFF',
843
+ padding: 16,
844
+ borderRadius: 12,
845
+ borderWidth: 1,
846
+ borderColor: '#E2E8F0',
847
+ gap: 4,
848
+ },
849
+ });`}
850
+ `;r(T.default.join(p,"Notification","styles.ts"),R);let W=`import React from 'react';
851
+ ${m}
852
+
853
+ export interface ProfileFormProps {
854
+ initialName?: string;
855
+ initialEmail?: string;
856
+ onSubmit: (data: { name: string; email: string }) => void;
857
+ loading?: boolean;
858
+ }
859
+
860
+ export function ProfileForm({ initialName = '', initialEmail = '', onSubmit, loading }: ProfileFormProps) {
861
+ const [name, setName] = React.useState(initialName);
862
+ const [email, setEmail] = React.useState(initialEmail);
863
+
864
+ return (
865
+ <CBox gap="md">
866
+ <CInput label="Full Name" value={name} onChangeText={setName} placeholder="Enter full name" />
867
+ <CInput label="Email Address" value={email} onChangeText={setEmail} placeholder="Enter email address" />
868
+ <CButton title="Save Profile" loading={loading} onPress={() => onSubmit({ name, email })} />
869
+ </CBox>
870
+ );
871
+ }
872
+ `;r(T.default.join(p,"Profile","form","index.tsx"),W);let Q=`import React from 'react';
873
+ import { ScrollView } from 'react-native';
874
+ ${u}
875
+ ${m}
876
+ ${d}
877
+ ${w}
878
+ import { ProfileForm } from './form';
879
+ import { styles } from './styles';
880
+
881
+ export function ProfileScreen() {
882
+ ${d?"const { user, logout } = useAuth();":""}
883
+ ${a&&c?`const { data: profile } = useProfileQuery();
884
+ const updateMutation = useUpdateProfileMutation();`:""}
885
+
886
+ const handleUpdate = (data: { name: string; email: string }) => {
887
+ ${a&&c?"updateMutation.mutate(data);":"ShowAlert({\n title: 'Profile Saved',\n message: `Profile updated for ${data.name}`,\n });"}
888
+ };
889
+
890
+ return (
891
+ <ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
892
+ <CBox padding="md" gap="lg">
893
+ <CText variant="header">Account Profile</CText>
894
+
895
+ <ProfileForm
896
+ initialName={${a&&c?"profile?.name ||":""} ${l?"user?.name ||":""} "Dharmendra"}
897
+ initialEmail={${a&&c?"profile?.email ||":""} ${l?"user?.email ||":""} "dharmendra@devminds.tech"}
898
+ onSubmit={handleUpdate}
899
+ loading={${a&&c?"updateMutation.isPending":"false"}}
900
+ />
901
+
902
+ ${l?'<CButton title="Log Out" variant="danger" onPress={logout} />':""}
903
+ </CBox>
904
+ </ScrollView>
905
+ );
906
+ }
907
+ `;r(T.default.join(p,"Profile","index.tsx"),Q);let q=`${u}
908
+
909
+ export const styles = ${o?`useStyles((r) => ({
910
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
911
+ contentContainer: { paddingVertical: r.vs(16) },
912
+ }));`:`StyleSheet.create({
913
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
914
+ contentContainer: { paddingVertical: 16 },
915
+ });`}
916
+ `;r(T.default.join(p,"Profile","styles.ts"),q);let re=`import React from 'react';
917
+ import { ScrollView } from 'react-native';
918
+ ${u}
919
+ ${h}
920
+ ${m}
921
+ ${d}
922
+ import { styles } from './styles';
923
+
924
+ export function SettingsScreen() {
925
+ ${n?"const { mode, setMode } = useTheme();":""}
926
+ ${l?"const { logout } = useAuth();":""}
927
+
928
+ const handleTriggerAlert = () => {
929
+ ${i?`ShowAlert({
930
+ title: 'Custom Cross-Platform Alert',
931
+ message: 'This alert component replaces native Alert.alert on iOS and Android for a uniform design.',
932
+ buttons: [
933
+ { text: 'Cancel', variant: 'outline' },
934
+ { text: 'Confirm', variant: 'primary', onPress: () => console.log('Confirmed') },
935
+ ],
936
+ });`:""}
937
+ };
938
+
939
+ return (
940
+ <ScrollView style={styles.container} contentContainerStyle={styles.contentContainer}>
941
+ <CBox padding="md" gap="lg">
942
+ <CText variant="header">Settings</CText>
943
+
944
+ ${n?`<CBox gap="sm">
945
+ <CText variant="subtitle">Appearance Theme</CText>
946
+ <CDropdown
947
+ label="Selected Mode"
948
+ value={mode}
949
+ options={[
950
+ { label: 'Light Mode', value: 'light' },
951
+ { label: 'Dark Mode', value: 'dark' },
952
+ { label: 'System Default', value: 'system' },
953
+ ]}
954
+ onChange={(val) => setMode(val as any)}
955
+ />
956
+ </CBox>`:""}
957
+
958
+ ${i?`<CBox gap="sm">
959
+ <CText variant="subtitle">Cross-Platform Alert Demo</CText>
960
+ <CButton title="Trigger ShowAlert" variant="outline" onPress={handleTriggerAlert} />
961
+ </CBox>`:""}
962
+
963
+ ${l?'<CButton title="Logout" variant="danger" onPress={logout} />':""}
964
+ </CBox>
965
+ </ScrollView>
966
+ );
967
+ }
968
+ `;r(T.default.join(p,"Settings","index.tsx"),re);let ne=`${u}
969
+
970
+ export const styles = ${o?`useStyles((r) => ({
971
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
972
+ contentContainer: { paddingVertical: r.vs(16) },
973
+ }));`:`StyleSheet.create({
974
+ container: { flex: 1, backgroundColor: '#F8FAFC' },
975
+ contentContainer: { paddingVertical: 16 },
976
+ });`}
977
+ `;r(T.default.join(p,"Settings","styles.ts"),ne);let G=`import React, { useState } from 'react';
978
+ import { View } from 'react-native';
979
+ ${u}
980
+ ${m}
981
+ ${d}
982
+ import { styles } from './styles';
983
+
984
+ export function LoginScreen() {
985
+ const [email, setEmail] = useState('');
986
+ const [password, setPassword] = useState('');
987
+ ${l?"const { login, continueAsGuest, isLoading } = useAuth();":""}
988
+
989
+ const handleLogin = () => {
990
+ ${l?'login(email || "dharmendra@devminds.tech", password);':'console.log("Login:", email);'}
991
+ };
992
+
993
+ return (
994
+ <View style={styles.container}>
995
+ <CBox padding="lg" gap="md" style={styles.card}>
996
+ <CText variant="header">DevMinds Tech</CText>
997
+ <CText variant="subtitle" color="#64748B">Sign in to your application</CText>
998
+
999
+ <CInput label="Email" placeholder="Enter email" value={email} onChangeText={setEmail} />
1000
+ <CInput label="Password" placeholder="Enter password" secureTextEntry value={password} onChangeText={setPassword} />
1001
+
1002
+ <CButton title="Login" loading={${l?"isLoading":"false"}} onPress={handleLogin} />
1003
+ ${l?'<CButton title="Continue as Guest" variant="outline" onPress={continueAsGuest} />':""}
1004
+ </CBox>
1005
+ </View>
1006
+ );
1007
+ }
1008
+ `;r(T.default.join(p,"Login","index.tsx"),G);let C=`${u}
1009
+
1010
+ export const styles = ${o?`useStyles((r) => ({
1011
+ container: { flex: 1, justifyContent: 'center', backgroundColor: '#F8FAFC', padding: r.ms(16) },
1012
+ card: { backgroundColor: '#FFFFFF', borderRadius: r.ms(16), borderWidth: 1, borderColor: '#E2E8F0' },
1013
+ }));`:`StyleSheet.create({
1014
+ container: { flex: 1, justifyContent: 'center', backgroundColor: '#F8FAFC', padding: 16 },
1015
+ card: { backgroundColor: '#FFFFFF', borderRadius: 16, borderWidth: 1, borderColor: '#E2E8F0' },
1016
+ });`}
1017
+ `;r(T.default.join(p,"Login","styles.ts"),C)}var A=x(require("path"));function ve(e){if(!e.components)return;let{targetDir:t,responsive:o,theme:n}=e,i=A.default.join(t,"src","components","common"),a=o?"import { useStyles, useResponsive } from 'react-native-dynamic-responsive';":"",l=`import React from 'react';
1018
+ import { Text, TextProps, TextStyle, StyleSheet } from 'react-native';
1019
+ ${a}
1020
+ ${n?"import { useTheme } from '@/theme';":""}
1021
+
1022
+ export type TextVariant = 'header' | 'title' | 'subtitle' | 'subHeader' | 'body' | 'label' | 'caption' | 'button';
1023
+
1024
+ export interface CTextProps extends TextProps {
1025
+ variant?: TextVariant;
1026
+ color?: string;
1027
+ children?: React.ReactNode;
1028
+ }
1029
+
1030
+ export function CText({ variant = 'body', color, style, children, ...rest }: CTextProps) {
1031
+ ${n?"const { theme } = useTheme();":""}
1032
+ const styles = ${o?`useStyles((r) => ({
1033
+ base: { fontSize: r.ms(14), color: '#0F172A' },
1034
+ header: { fontSize: r.ms(24), fontWeight: 'bold' },
1035
+ title: { fontSize: r.ms(20), fontWeight: '700' },
1036
+ subtitle: { fontSize: r.ms(16), fontWeight: '600' },
1037
+ subHeader: { fontSize: r.ms(18), fontWeight: '600' },
1038
+ body: { fontSize: r.ms(14), fontWeight: '400' },
1039
+ label: { fontSize: r.ms(13), fontWeight: '500' },
1040
+ caption: { fontSize: r.ms(12), color: '#64748B' },
1041
+ button: { fontSize: r.ms(15), fontWeight: '600' },
1042
+ }));`:"staticStyles;"}
1043
+
1044
+ const textColor = color || ${n?"theme.colors.text":"'#0F172A'"};
1045
+
1046
+ return (
1047
+ <Text style={[styles.base, styles[variant], { color: textColor }, style]} {...rest}>
1048
+ {children}
1049
+ </Text>
1050
+ );
1051
+ }
1052
+
1053
+ ${o?"":`const staticStyles = StyleSheet.create({
1054
+ base: { fontSize: 14, color: '#0F172A' },
1055
+ header: { fontSize: 24, fontWeight: 'bold' },
1056
+ title: { fontSize: 20, fontWeight: '700' },
1057
+ subtitle: { fontSize: 16, fontWeight: '600' },
1058
+ subHeader: { fontSize: 18, fontWeight: '600' },
1059
+ body: { fontSize: 14, fontWeight: '400' },
1060
+ label: { fontSize: 13, fontWeight: '500' },
1061
+ caption: { fontSize: 12, color: '#64748B' },
1062
+ button: { fontSize: 15, fontWeight: '600' },
1063
+ });`}
1064
+ `;r(A.default.join(i,"CText.tsx"),l);let p=`import React from 'react';
1065
+ import { View, ViewProps, ViewStyle } from 'react-native';
1066
+ ${a}
1067
+
1068
+ export interface CBoxProps extends ViewProps {
1069
+ padding?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number;
1070
+ margin?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number;
1071
+ gap?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | number;
1072
+ flex?: number;
1073
+ row?: boolean;
1074
+ align?: ViewStyle['alignItems'];
1075
+ justify?: ViewStyle['justifyContent'];
1076
+ children?: React.ReactNode;
1077
+ }
1078
+
1079
+ const spacingMap: Record<string, number> = {
1080
+ xs: 4,
1081
+ sm: 8,
1082
+ md: 16,
1083
+ lg: 24,
1084
+ xl: 32,
1085
+ };
1086
+
1087
+ export function CBox({
1088
+ padding,
1089
+ margin,
1090
+ gap,
1091
+ flex,
1092
+ row,
1093
+ align,
1094
+ justify,
1095
+ style,
1096
+ children,
1097
+ ...rest
1098
+ }: CBoxProps) {
1099
+ ${o?"const r = useResponsive();":""}
1100
+
1101
+ const resolveVal = (val?: string | number) => {
1102
+ if (typeof val === 'number') return ${o?"r.ms(val)":"val"};
1103
+ if (typeof val === 'string' && spacingMap[val]) return ${o?"r.ms(spacingMap[val])":"spacingMap[val]"};
1104
+ return undefined;
1105
+ };
1106
+
1107
+ const computedStyle: ViewStyle = {
1108
+ flex,
1109
+ flexDirection: row ? 'row' : 'column',
1110
+ alignItems: align,
1111
+ justifyContent: justify,
1112
+ padding: resolveVal(padding),
1113
+ margin: resolveVal(margin),
1114
+ gap: resolveVal(gap),
1115
+ };
1116
+
1117
+ return (
1118
+ <View style={[computedStyle, style]} {...rest}>
1119
+ {children}
1120
+ </View>
1121
+ );
1122
+ }
1123
+ `;r(A.default.join(i,"CBox.tsx"),p);let u=`import React from 'react';
1124
+ import { TextInput, TextInputProps, StyleSheet } from 'react-native';
1125
+ import { CText } from './CText';
1126
+ import { CBox } from './CBox';
1127
+ ${a}
1128
+
1129
+ export interface CInputProps extends TextInputProps {
1130
+ label?: string;
1131
+ error?: string;
1132
+ disabled?: boolean;
1133
+ }
1134
+
1135
+ export function CInput({ label, error, disabled, style, ...rest }: CInputProps) {
1136
+ const styles = ${o?`useStyles((r) => ({
1137
+ input: {
1138
+ height: r.ms(48),
1139
+ borderWidth: 1,
1140
+ borderColor: '#CBD5E1',
1141
+ borderRadius: r.ms(8),
1142
+ paddingHorizontal: r.ms(12),
1143
+ fontSize: r.ms(14),
1144
+ color: '#0F172A',
1145
+ backgroundColor: '#FFFFFF',
1146
+ },
1147
+ inputError: { borderColor: '#EF4444' },
1148
+ disabled: { backgroundColor: '#F1F5F9', color: '#94A3B8' },
1149
+ }));`:"staticStyles;"}
1150
+
1151
+ return (
1152
+ <CBox gap="xs">
1153
+ {label ? <CText variant="label">{label}</CText> : null}
1154
+ <TextInput
1155
+ style={[styles.input, error ? styles.inputError : null, disabled ? styles.disabled : null, style]}
1156
+ editable={!disabled}
1157
+ placeholderTextColor="#94A3B8"
1158
+ {...rest}
1159
+ />
1160
+ {error ? <CText variant="caption" color="#EF4444">{error}</CText> : null}
1161
+ </CBox>
1162
+ );
1163
+ }
1164
+
1165
+ ${o?"":`const staticStyles = StyleSheet.create({
1166
+ input: {
1167
+ height: 48,
1168
+ borderWidth: 1,
1169
+ borderColor: '#CBD5E1',
1170
+ borderRadius: 8,
1171
+ paddingHorizontal: 12,
1172
+ fontSize: 14,
1173
+ color: '#0F172A',
1174
+ backgroundColor: '#FFFFFF',
1175
+ },
1176
+ inputError: { borderColor: '#EF4444' },
1177
+ disabled: { backgroundColor: '#F1F5F9', color: '#94A3B8' },
1178
+ });`}
1179
+ `;r(A.default.join(i,"CInput.tsx"),u);let h=`import React from 'react';
1180
+ import { TouchableOpacity, ActivityIndicator, TouchableOpacityProps, StyleSheet } from 'react-native';
1181
+ import { CText } from './CText';
1182
+ ${a}
1183
+
1184
+ export type ButtonVariant = 'primary' | 'secondary' | 'outline' | 'danger';
1185
+
1186
+ export interface CButtonProps extends TouchableOpacityProps {
1187
+ title: string;
1188
+ variant?: ButtonVariant;
1189
+ loading?: boolean;
1190
+ disabled?: boolean;
1191
+ }
1192
+
1193
+ export function CButton({
1194
+ title,
1195
+ variant = 'primary',
1196
+ loading,
1197
+ disabled,
1198
+ style,
1199
+ onPress,
1200
+ ...rest
1201
+ }: CButtonProps) {
1202
+ const styles = ${o?`useStyles((r) => ({
1203
+ btn: {
1204
+ height: r.ms(48),
1205
+ borderRadius: r.ms(8),
1206
+ justifyContent: 'center',
1207
+ alignItems: 'center',
1208
+ paddingHorizontal: r.ms(16),
1209
+ },
1210
+ primary: { backgroundColor: '#0284C7' },
1211
+ secondary: { backgroundColor: '#64748B' },
1212
+ outline: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#0284C7' },
1213
+ danger: { backgroundColor: '#EF4444' },
1214
+ disabled: { opacity: 0.6 },
1215
+ text: { fontSize: r.ms(15), fontWeight: '600' },
1216
+ primaryText: { color: '#FFFFFF' },
1217
+ secondaryText: { color: '#FFFFFF' },
1218
+ outlineText: { color: '#0284C7' },
1219
+ dangerText: { color: '#FFFFFF' },
1220
+ }));`:"staticStyles;"}
1221
+
1222
+ const isDisabled = disabled || loading;
1223
+
1224
+ return (
1225
+ <TouchableOpacity
1226
+ style={[styles.btn, styles[variant], isDisabled ? styles.disabled : null, style]}
1227
+ activeOpacity={0.8}
1228
+ disabled={isDisabled}
1229
+ onPress={onPress}
1230
+ {...rest}
1231
+ >
1232
+ {loading ? (
1233
+ <ActivityIndicator color={variant === 'outline' ? '#0284C7' : '#FFFFFF'} />
1234
+ ) : (
1235
+ <CText style={[styles.text, styles[\`\${variant}Text\` as keyof typeof styles]]}>{title}</CText>
1236
+ )}
1237
+ </TouchableOpacity>
1238
+ );
1239
+ }
1240
+
1241
+ ${o?"":`const staticStyles = StyleSheet.create({
1242
+ btn: {
1243
+ height: 48,
1244
+ borderRadius: 8,
1245
+ justifyContent: 'center',
1246
+ alignItems: 'center',
1247
+ paddingHorizontal: 16,
1248
+ },
1249
+ primary: { backgroundColor: '#0284C7' },
1250
+ secondary: { backgroundColor: '#64748B' },
1251
+ outline: { backgroundColor: 'transparent', borderWidth: 1, borderColor: '#0284C7' },
1252
+ danger: { backgroundColor: '#EF4444' },
1253
+ disabled: { opacity: 0.6 },
1254
+ text: { fontSize: 15, fontWeight: '600' },
1255
+ primaryText: { color: '#FFFFFF' },
1256
+ secondaryText: { color: '#FFFFFF' },
1257
+ outlineText: { color: '#0284C7' },
1258
+ dangerText: { color: '#FFFFFF' },
1259
+ });`}
1260
+ `;r(A.default.join(i,"CButton.tsx"),h);let m=`import React, { useState } from 'react';
1261
+ import { View, TouchableOpacity, Modal, FlatList, StyleSheet } from 'react-native';
1262
+ import { CText } from './CText';
1263
+ import { CBox } from './CBox';
1264
+ ${a}
1265
+
1266
+ export interface DropdownOption {
1267
+ label: string;
1268
+ value: string | number;
1269
+ }
1270
+
1271
+ export interface CDropdownProps {
1272
+ label?: string;
1273
+ options: DropdownOption[];
1274
+ value?: string | number;
1275
+ onChange: (val: string | number) => void;
1276
+ disabled?: boolean;
1277
+ error?: string;
1278
+ }
1279
+
1280
+ export function CDropdown({ label, options, value, onChange, disabled, error }: CDropdownProps) {
1281
+ const [visible, setVisible] = useState(false);
1282
+ const styles = ${o?`useStyles((r) => ({
1283
+ box: {
1284
+ height: r.ms(48),
1285
+ borderWidth: 1,
1286
+ borderColor: '#CBD5E1',
1287
+ borderRadius: r.ms(8),
1288
+ paddingHorizontal: r.ms(12),
1289
+ flexDirection: 'row',
1290
+ alignItems: 'center',
1291
+ justifyContent: 'space-between',
1292
+ backgroundColor: '#FFFFFF',
1293
+ },
1294
+ errorBox: { borderColor: '#EF4444' },
1295
+ disabled: { backgroundColor: '#F1F5F9' },
1296
+ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)', justifyContent: 'center', padding: r.ms(24) },
1297
+ modalContent: { backgroundColor: '#FFFFFF', borderRadius: r.ms(12), maxHeight: r.ms(300), padding: r.ms(8) },
1298
+ optionItem: { paddingVertical: r.ms(12), paddingHorizontal: r.ms(16) },
1299
+ selectedText: { fontWeight: 'bold', color: '#0284C7' },
1300
+ }));`:"staticStyles;"}
1301
+
1302
+ const selectedOption = options.find((opt) => opt.value === value);
1303
+
1304
+ return (
1305
+ <CBox gap="xs">
1306
+ {label ? <CText variant="label">{label}</CText> : null}
1307
+ <TouchableOpacity
1308
+ style={[styles.box, error ? styles.errorBox : null, disabled ? styles.disabled : null]}
1309
+ disabled={disabled}
1310
+ onPress={() => setVisible(true)}
1311
+ >
1312
+ <CText>{selectedOption ? selectedOption.label : 'Select an option'}</CText>
1313
+ <CText color="#64748B">\u25BC</CText>
1314
+ </TouchableOpacity>
1315
+
1316
+ <Modal visible={visible} transparent animationType="fade" onRequestClose={() => setVisible(false)}>
1317
+ <TouchableOpacity style={styles.overlay} activeOpacity={1} onPress={() => setVisible(false)}>
1318
+ <View style={styles.modalContent}>
1319
+ <FlatList
1320
+ data={options}
1321
+ keyExtractor={(item) => String(item.value)}
1322
+ renderItem={({ item }) => (
1323
+ <TouchableOpacity
1324
+ style={styles.optionItem}
1325
+ onPress={() => {
1326
+ onChange(item.value);
1327
+ setVisible(false);
1328
+ }}
1329
+ >
1330
+ <CText style={item.value === value ? styles.selectedText : null}>{item.label}</CText>
1331
+ </TouchableOpacity>
1332
+ )}
1333
+ />
1334
+ </View>
1335
+ </TouchableOpacity>
1336
+ </Modal>
1337
+ {error ? <CText variant="caption" color="#EF4444">{error}</CText> : null}
1338
+ </CBox>
1339
+ );
1340
+ }
1341
+
1342
+ ${o?"":`const staticStyles = StyleSheet.create({
1343
+ box: {
1344
+ height: 48,
1345
+ borderWidth: 1,
1346
+ borderColor: '#CBD5E1',
1347
+ borderRadius: 8,
1348
+ paddingHorizontal: 12,
1349
+ flexDirection: 'row',
1350
+ alignItems: 'center',
1351
+ justifyContent: 'space-between',
1352
+ backgroundColor: '#FFFFFF',
1353
+ },
1354
+ errorBox: { borderColor: '#EF4444' },
1355
+ disabled: { backgroundColor: '#F1F5F9' },
1356
+ overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.4)', justifyContent: 'center', padding: 24 },
1357
+ modalContent: { backgroundColor: '#FFFFFF', borderRadius: 12, maxHeight: 300, padding: 8 },
1358
+ optionItem: { paddingVertical: 12, paddingHorizontal: 16 },
1359
+ selectedText: { fontWeight: 'bold', color: '#0284C7' },
1360
+ });`}
1361
+ `;r(A.default.join(i,"CDropdown.tsx"),m);let w=`import React, { createContext, useContext, useState, useCallback } from 'react';
1362
+ import { Modal, View, TouchableOpacity, StyleSheet } from 'react-native';
1363
+ import { CText } from './CText';
1364
+ import { CButton, ButtonVariant } from './CButton';
1365
+ import { CBox } from './CBox';
1366
+ ${a}
1367
+
1368
+ export interface AlertButton {
1369
+ text: string;
1370
+ onPress?: () => void;
1371
+ variant?: ButtonVariant;
1372
+ }
1373
+
1374
+ export interface AlertOptions {
1375
+ title: string;
1376
+ message?: string;
1377
+ type?: 'info' | 'success' | 'warning' | 'error';
1378
+ buttons?: AlertButton[];
1379
+ }
1380
+
1381
+ interface AlertContextType {
1382
+ showAlert: (options: AlertOptions) => void;
1383
+ hideAlert: () => void;
1384
+ }
1385
+
1386
+ const AlertContext = createContext<AlertContextType | undefined>(undefined);
1387
+
1388
+ let globalShowAlert: ((options: AlertOptions) => void) | null = null;
1389
+
1390
+ export function ShowAlert(options: AlertOptions) {
1391
+ if (globalShowAlert) {
1392
+ globalShowAlert(options);
1393
+ } else {
1394
+ console.warn('[ShowAlert] AlertProvider is not mounted in App.tsx.');
1395
+ }
1396
+ }
1397
+
1398
+ export function AlertProvider({ children }: { children: React.ReactNode }) {
1399
+ const [alertConfig, setAlertConfig] = useState<AlertOptions | null>(null);
1400
+
1401
+ const showAlert = useCallback((options: AlertOptions) => {
1402
+ setAlertConfig(options);
1403
+ }, []);
1404
+
1405
+ const hideAlert = useCallback(() => {
1406
+ setAlertConfig(null);
1407
+ }, []);
1408
+
1409
+ globalShowAlert = showAlert;
1410
+
1411
+ const styles = ${o?`useStyles((r) => ({
1412
+ overlay: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.6)', justifyContent: 'center', alignItems: 'center', padding: r.ms(24) },
1413
+ container: { backgroundColor: '#FFFFFF', borderRadius: r.ms(16), padding: r.ms(20), width: '100%', maxWidth: r.ms(340), gap: r.ms(16) },
1414
+ buttonRow: { flexDirection: 'row', gap: r.ms(8), justifyContent: 'flex-end', marginTop: r.ms(8) },
1415
+ }));`:"staticStyles;"}
1416
+
1417
+ const buttons = alertConfig?.buttons?.length
1418
+ ? alertConfig.buttons
1419
+ : [{ text: 'OK', variant: 'primary' as const }];
1420
+
1421
+ return (
1422
+ <AlertContext.Provider value={{ showAlert, hideAlert }}>
1423
+ {children}
1424
+ {alertConfig ? (
1425
+ <Modal visible transparent animationType="fade" onRequestClose={hideAlert}>
1426
+ <TouchableOpacity style={styles.overlay} activeOpacity={1} onPress={hideAlert}>
1427
+ <TouchableOpacity activeOpacity={1} style={styles.container}>
1428
+ <CBox gap="xs">
1429
+ <CText variant="title">{alertConfig.title}</CText>
1430
+ {alertConfig.message ? <CText variant="body" color="#64748B">{alertConfig.message}</CText> : null}
1431
+ </CBox>
1432
+ <View style={styles.buttonRow}>
1433
+ {buttons.map((btn, index) => (
1434
+ <CButton
1435
+ key={index}
1436
+ title={btn.text}
1437
+ variant={btn.variant || (index === buttons.length - 1 ? 'primary' : 'outline')}
1438
+ style={{ flex: buttons.length > 1 ? 1 : undefined }}
1439
+ onPress={() => {
1440
+ hideAlert();
1441
+ if (btn.onPress) btn.onPress();
1442
+ }}
1443
+ />
1444
+ ))}
1445
+ </View>
1446
+ </TouchableOpacity>
1447
+ </TouchableOpacity>
1448
+ </Modal>
1449
+ ) : null}
1450
+ </AlertContext.Provider>
1451
+ );
1452
+ }
1453
+
1454
+ export function useAlert(): AlertContextType {
1455
+ const context = useContext(AlertContext);
1456
+ if (!context) {
1457
+ throw new Error('useAlert must be used within an AlertProvider');
1458
+ }
1459
+ return context;
1460
+ }
1461
+
1462
+ ${o?"":`const staticStyles = StyleSheet.create({
1463
+ overlay: { flex: 1, backgroundColor: 'rgba(15, 23, 42, 0.6)', justifyContent: 'center', alignItems: 'center', padding: 24 },
1464
+ container: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 20, width: '100%', maxWidth: 340, gap: 16 },
1465
+ buttonRow: { flexDirection: 'row', gap: 8, justifyContent: 'flex-end', marginTop: 8 },
1466
+ });`}
1467
+ `;r(A.default.join(i,"ShowAlert.tsx"),w),r(A.default.join(i,"UIStates.tsx"),`import React from 'react';
1468
+ import { View, ActivityIndicator, StyleSheet } from 'react-native';
1469
+ import { CText } from './CText';
1470
+ import { CButton } from './CButton';
1471
+ import { CBox } from './CBox';
1472
+
1473
+ export function CLoading({ message = 'Loading...' }: { message?: string }) {
1474
+ return (
1475
+ <View style={styles.centerContainer}>
1476
+ <ActivityIndicator size="large" color="#0284C7" />
1477
+ <CText variant="body" color="#64748B">{message}</CText>
1478
+ </View>
1479
+ );
1480
+ }
1481
+
1482
+ export function CError({ message = 'Something went wrong.', onRetry }: { message?: string; onRetry?: () => void }) {
1483
+ return (
1484
+ <View style={styles.centerContainer}>
1485
+ <CBox gap="md" align="center">
1486
+ <CText variant="subtitle" color="#EF4444">{message}</CText>
1487
+ {onRetry ? <CButton title="Try Again" variant="outline" onPress={onRetry} /> : null}
1488
+ </CBox>
1489
+ </View>
1490
+ );
1491
+ }
1492
+
1493
+ export function CEmpty({ title = 'No Data Found', message }: { title?: string; message?: string }) {
1494
+ return (
1495
+ <View style={styles.centerContainer}>
1496
+ <CBox gap="xs" align="center">
1497
+ <CText variant="title">{title}</CText>
1498
+ {message ? <CText variant="caption" color="#94A3B8">{message}</CText> : null}
1499
+ </CBox>
1500
+ </View>
1501
+ );
1502
+ }
1503
+
1504
+ const styles = StyleSheet.create({
1505
+ centerContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 24, gap: 12 },
1506
+ });
1507
+ `),r(A.default.join(t,"src","components","index.ts"),`export * from './common/CText';
1508
+ export * from './common/CInput';
1509
+ export * from './common/CBox';
1510
+ export * from './common/CDropdown';
1511
+ export * from './common/CButton';
1512
+ export * from './common/ShowAlert';
1513
+ export * from './common/UIStates';
1514
+ `)}var P=x(require("path"));function be(e){if(!e.theme)return;let{targetDir:t,storage:o}=e,n=P.default.join(t,"src","theme");r(P.default.join(n,"colors.ts"),`export const lightColors = {
1515
+ primary: '#0284C7',
1516
+ background: '#F8FAFC',
1517
+ card: '#FFFFFF',
1518
+ text: '#0F172A',
1519
+ textSecondary: '#64748B',
1520
+ border: '#E2E8F0',
1521
+ error: '#EF4444',
1522
+ success: '#10B981',
1523
+ warning: '#F59E0B',
1524
+ };
1525
+
1526
+ export const darkColors: typeof lightColors = {
1527
+ primary: '#38BDF8',
1528
+ background: '#0F172A',
1529
+ card: '#1E293B',
1530
+ text: '#F8FAFC',
1531
+ textSecondary: '#94A3B8',
1532
+ border: '#334155',
1533
+ error: '#F87171',
1534
+ success: '#34D399',
1535
+ warning: '#FBBF24',
1536
+ };
1537
+ `),r(P.default.join(n,"spacing.ts"),`import { variable } from '@/constant';
1538
+
1539
+ export const spacing = variable.spacing;
1540
+ `),r(P.default.join(n,"typography.ts"),`import { variable } from '@/constant';
1541
+
1542
+ export const typography = {
1543
+ fontSizes: variable.font,
1544
+ fontWeights: {
1545
+ regular: '400' as const,
1546
+ medium: '500' as const,
1547
+ semibold: '600' as const,
1548
+ bold: '700' as const,
1549
+ },
1550
+ };
1551
+ `),r(P.default.join(n,"radius.ts"),`export const radius = {
1552
+ xs: 4,
1553
+ sm: 8,
1554
+ md: 12,
1555
+ lg: 16,
1556
+ full: 9999,
1557
+ };
1558
+ `),r(P.default.join(n,"shadows.ts"),`export const shadows = {
1559
+ sm: {
1560
+ shadowColor: '#000',
1561
+ shadowOffset: { width: 0, height: 1 },
1562
+ shadowOpacity: 0.05,
1563
+ shadowRadius: 2,
1564
+ elevation: 2,
1565
+ },
1566
+ md: {
1567
+ shadowColor: '#000',
1568
+ shadowOffset: { width: 0, height: 2 },
1569
+ shadowOpacity: 0.1,
1570
+ shadowRadius: 4,
1571
+ elevation: 4,
1572
+ },
1573
+ };
1574
+ `),r(P.default.join(n,"types.ts"),`import { lightColors } from './colors';
1575
+ import { spacing } from './spacing';
1576
+ import { typography } from './typography';
1577
+ import { radius } from './radius';
1578
+ import { shadows } from './shadows';
1579
+
1580
+ export type ThemeMode = 'light' | 'dark' | 'system';
1581
+
1582
+ export interface Theme {
1583
+ colors: typeof lightColors;
1584
+ spacing: typeof spacing;
1585
+ typography: typeof typography;
1586
+ radius: typeof radius;
1587
+ shadows: typeof shadows;
1588
+ isDark: boolean;
1589
+ }
1590
+ `);let h=`import React, { createContext, useContext, useState, useEffect } from 'react';
1591
+ import { useColorScheme } from 'react-native';
1592
+ import { lightColors, darkColors } from './colors';
1593
+ import { spacing } from './spacing';
1594
+ import { typography } from './typography';
1595
+ import { radius } from './radius';
1596
+ import { shadows } from './shadows';
1597
+ import { Theme, ThemeMode } from './types';
1598
+ ${o?"import { storage } from '@/storage';":""}
1599
+
1600
+ interface ThemeContextType {
1601
+ theme: Theme;
1602
+ mode: ThemeMode;
1603
+ setMode: (mode: ThemeMode) => void;
1604
+ }
1605
+
1606
+ const THEME_STORAGE_KEY = '@app_theme_mode';
1607
+
1608
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
1609
+
1610
+ export function AppTheme({ children }: { children: React.ReactNode }) {
1611
+ const systemScheme = useColorScheme();
1612
+ const [mode, setModeState] = useState<ThemeMode>('system');
1613
+
1614
+ useEffect(() => {
1615
+ ${o?`storage.get<ThemeMode>(THEME_STORAGE_KEY).then((saved) => {
1616
+ if (saved) setModeState(saved);
1617
+ });`:""}
1618
+ }, []);
1619
+
1620
+ const setMode = (newMode: ThemeMode) => {
1621
+ setModeState(newMode);
1622
+ ${o?"storage.set(THEME_STORAGE_KEY, newMode);":""}
1623
+ };
1624
+
1625
+ const isDark = mode === 'system' ? systemScheme === 'dark' : mode === 'dark';
1626
+
1627
+ const theme: Theme = {
1628
+ colors: isDark ? darkColors : lightColors,
1629
+ spacing,
1630
+ typography,
1631
+ radius,
1632
+ shadows,
1633
+ isDark,
1634
+ };
1635
+
1636
+ return <ThemeContext.Provider value={{ theme, mode, setMode }}>{children}</ThemeContext.Provider>;
1637
+ }
1638
+
1639
+ export function useTheme(): ThemeContextType {
1640
+ const context = useContext(ThemeContext);
1641
+ if (!context) {
1642
+ throw new Error('useTheme must be used within an AppTheme provider');
1643
+ }
1644
+ return context;
1645
+ }
1646
+ `;r(P.default.join(n,"AppTheme.tsx"),h),r(P.default.join(n,"index.ts"),`export * from './AppTheme';
1647
+ export * from './colors';
1648
+ export * from './spacing';
1649
+ export * from './typography';
1650
+ export * from './radius';
1651
+ export * from './shadows';
1652
+ export * from './types';
1653
+ `)}var O=x(require("path"));function Ce(e){if(!e.auth)return;let{targetDir:t,storage:o,guest:n}=e,i=O.default.join(t,"src","auth"),a=`export interface User {
1654
+ id: string;
1655
+ name: string;
1656
+ email: string;
1657
+ avatarUrl?: string;
1658
+ }
1659
+
1660
+ export interface AuthState {
1661
+ user: User | null;
1662
+ token: string | null;
1663
+ isAuthenticated: boolean;
1664
+ isGuest: boolean;
1665
+ isLoading: boolean;
1666
+ }
1667
+
1668
+ export interface AuthContextType extends AuthState {
1669
+ login: (email: string, password?: string) => Promise<void>;
1670
+ logout: () => Promise<void>;
1671
+ ${n?"continueAsGuest: () => void;":""}
1672
+ }
1673
+ `;r(O.default.join(i,"types.ts"),a),r(O.default.join(i,"AuthContext.ts"),`import { createContext } from 'react';
1674
+ import { AuthContextType } from './types';
1675
+
1676
+ export const AuthContext = createContext<AuthContextType | undefined>(undefined);
1677
+ `);let l=`import React, { useState, useEffect } from 'react';
1678
+ import { AuthContext } from './AuthContext';
1679
+ import { User, AuthState } from './types';
1680
+ ${o?"import { storage } from '@/storage';":""}
1681
+
1682
+ const AUTH_TOKEN_KEY = '@auth_user_token';
1683
+ const AUTH_USER_KEY = '@auth_user_data';
1684
+
1685
+ export function AuthProvider({ children }: { children: React.ReactNode }) {
1686
+ const [state, setState] = useState<AuthState>({
1687
+ user: null,
1688
+ token: null,
1689
+ isAuthenticated: false,
1690
+ isGuest: false,
1691
+ isLoading: true,
1692
+ });
1693
+
1694
+ useEffect(() => {
1695
+ restoreSession();
1696
+ }, []);
1697
+
1698
+ const restoreSession = async () => {
1699
+ try {
1700
+ ${o?`const token = await storage.get<string>(AUTH_TOKEN_KEY);
1701
+ const user = await storage.get<User>(AUTH_USER_KEY);
1702
+ if (token && user) {
1703
+ setState({
1704
+ user,
1705
+ token,
1706
+ isAuthenticated: true,
1707
+ isGuest: false,
1708
+ isLoading: false,
1709
+ });
1710
+ return;
1711
+ }`:""}
1712
+ } catch {
1713
+ // Ignore error during session restore
1714
+ } finally {
1715
+ setState((prev) => ({ ...prev, isLoading: false }));
1716
+ }
1717
+ };
1718
+
1719
+ const login = async (email: string, password?: string) => {
1720
+ setState((prev) => ({ ...prev, isLoading: true }));
1721
+ try {
1722
+ const mockUser: User = { id: 'usr_1001', name: 'John Doe', email };
1723
+ const mockToken = 'demo-bearer-token-123456';
1724
+
1725
+ ${o?`await storage.set(AUTH_TOKEN_KEY, mockToken);
1726
+ await storage.set(AUTH_USER_KEY, mockUser);`:""}
1727
+
1728
+ setState({
1729
+ user: mockUser,
1730
+ token: mockToken,
1731
+ isAuthenticated: true,
1732
+ isGuest: false,
1733
+ isLoading: false,
1734
+ });
1735
+ } catch (err) {
1736
+ setState((prev) => ({ ...prev, isLoading: false }));
1737
+ throw err;
1738
+ }
1739
+ };
1740
+
1741
+ const logout = async () => {
1742
+ setState((prev) => ({ ...prev, isLoading: true }));
1743
+ try {
1744
+ ${o?`await storage.remove(AUTH_TOKEN_KEY);
1745
+ await storage.remove(AUTH_USER_KEY);`:""}
1746
+ } finally {
1747
+ setState({
1748
+ user: null,
1749
+ token: null,
1750
+ isAuthenticated: false,
1751
+ isGuest: false,
1752
+ isLoading: false,
1753
+ });
1754
+ }
1755
+ };
1756
+
1757
+ ${n?`const continueAsGuest = () => {
1758
+ setState({
1759
+ user: null,
1760
+ token: null,
1761
+ isAuthenticated: false,
1762
+ isGuest: true,
1763
+ isLoading: false,
1764
+ });
1765
+ };`:""}
1766
+
1767
+ return (
1768
+ <AuthContext.Provider
1769
+ value={{
1770
+ ...state,
1771
+ login,
1772
+ logout,
1773
+ ${n?"continueAsGuest,":""}
1774
+ }}
1775
+ >
1776
+ {children}
1777
+ </AuthContext.Provider>
1778
+ );
1779
+ }
1780
+ `;r(O.default.join(i,"AuthProvider.tsx"),l),r(O.default.join(i,"useAuth.ts"),`import { useContext } from 'react';
1781
+ import { AuthContext } from './AuthContext';
1782
+ import { AuthContextType } from './types';
1783
+
1784
+ export function useAuth(): AuthContextType {
1785
+ const context = useContext(AuthContext);
1786
+ if (!context) {
1787
+ throw new Error('useAuth must be used within an AuthProvider');
1788
+ }
1789
+ return context;
1790
+ }
1791
+ `),r(O.default.join(i,"index.ts"),`export * from './AuthProvider';
1792
+ export * from './AuthContext';
1793
+ export * from './useAuth';
1794
+ export * from './types';
1795
+ `)}var Z=x(require("path"));function Se(e){if(!e.storage)return;let{targetDir:t}=e,o=Z.default.join(t,"src","storage");r(Z.default.join(o,"index.ts"),`import AsyncStorage from '@react-native-async-storage/async-storage';
1796
+
1797
+ export const storage = {
1798
+ async set(key: string, value: any): Promise<void> {
1799
+ try {
1800
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
1801
+ await AsyncStorage.setItem(key, stringValue);
1802
+ } catch (error) {
1803
+ console.error(\`[Storage] Error setting key "\${key}":\`, error);
1804
+ }
1805
+ },
1806
+
1807
+ async get<T = any>(key: string): Promise<T | null> {
1808
+ try {
1809
+ const value = await AsyncStorage.getItem(key);
1810
+ if (!value) return null;
1811
+ try {
1812
+ return JSON.parse(value) as T;
1813
+ } catch {
1814
+ return value as unknown as T;
1815
+ }
1816
+ } catch (error) {
1817
+ console.error(\`[Storage] Error getting key "\${key}":\`, error);
1818
+ return null;
1819
+ }
1820
+ },
1821
+
1822
+ async remove(key: string): Promise<void> {
1823
+ try {
1824
+ await AsyncStorage.removeItem(key);
1825
+ } catch (error) {
1826
+ console.error(\`[Storage] Error removing key "\${key}":\`, error);
1827
+ }
1828
+ },
1829
+
1830
+ async clear(): Promise<void> {
1831
+ try {
1832
+ await AsyncStorage.clear();
1833
+ } catch (error) {
1834
+ console.error('[Storage] Error clearing storage:', error);
1835
+ }
1836
+ },
1837
+ };
1838
+ `)}var z=x(require("path"));function Te(e){let{targetDir:t}=e,o=z.default.join(t,"src","constant");r(z.default.join(o,"variable.ts"),`export const variable = {
1839
+ padding: {
1840
+ xs: 4,
1841
+ sm: 8,
1842
+ md: 16,
1843
+ lg: 24,
1844
+ xl: 32,
1845
+ },
1846
+ spacing: {
1847
+ xs: 4,
1848
+ sm: 8,
1849
+ md: 16,
1850
+ lg: 24,
1851
+ xl: 32,
1852
+ },
1853
+ header: {
1854
+ height: 56,
1855
+ },
1856
+ font: {
1857
+ small: 12,
1858
+ body: 14,
1859
+ subtitle: 16,
1860
+ title: 20,
1861
+ header: 24,
1862
+ },
1863
+ };
1864
+ `),r(z.default.join(o,"index.ts"),`export * from './variable';
1865
+ `)}var B=x(require("path"));function Fe(e){let{targetDir:t}=e,o=B.default.join(t,"src","utils");r(B.default.join(o,"dateFormat.ts"),`export function formatDate(date: Date | string | number): string {
1866
+ const d = new Date(date);
1867
+ if (isNaN(d.getTime())) return '';
1868
+ return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
1869
+ }
1870
+
1871
+ export function formatDateTime(date: Date | string | number): string {
1872
+ const d = new Date(date);
1873
+ if (isNaN(d.getTime())) return '';
1874
+ return d.toLocaleString('en-US', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' });
1875
+ }
1876
+
1877
+ export function formatRelativeDate(date: Date | string | number): string {
1878
+ const d = new Date(date);
1879
+ if (isNaN(d.getTime())) return '';
1880
+ const now = new Date();
1881
+ const diffInSeconds = Math.floor((now.getTime() - d.getTime()) / 1000);
1882
+
1883
+ if (diffInSeconds < 60) return 'just now';
1884
+ if (diffInSeconds < 3600) return \`\${Math.floor(diffInSeconds / 60)}m ago\`;
1885
+ if (diffInSeconds < 86400) return \`\${Math.floor(diffInSeconds / 3600)}h ago\`;
1886
+ return formatDate(d);
1887
+ }
1888
+ `),r(B.default.join(o,"mobileNumber.ts"),`export function formatMobileNumber(phone: string, countryCode = '+1'): string {
1889
+ const cleaned = phone.replace(/\\D/g, '');
1890
+ if (cleaned.length === 10) {
1891
+ return \`\${countryCode} (\${cleaned.slice(0, 3)}) \${cleaned.slice(3, 6)}-\${cleaned.slice(6)}\`;
1892
+ }
1893
+ return phone;
1894
+ }
1895
+
1896
+ export function validateMobileNumber(phone: string): boolean {
1897
+ const cleaned = phone.replace(/\\D/g, '');
1898
+ return cleaned.length >= 7 && cleaned.length <= 15;
1899
+ }
1900
+
1901
+ export function normalizeMobileNumber(phone: string): string {
1902
+ return phone.replace(/\\D/g, '');
1903
+ }
1904
+ `),r(B.default.join(o,"currency.ts"),`export function formatCurrency(amount: number, currencyCode = 'USD', locale = 'en-US'): string {
1905
+ try {
1906
+ return new Intl.NumberFormat(locale, { style: 'currency', currency: currencyCode }).format(amount);
1907
+ } catch {
1908
+ return \`$\${amount.toFixed(2)}\`;
1909
+ }
1910
+ }
1911
+ `),r(B.default.join(o,"validation.ts"),`export function isValidEmail(email: string): boolean {
1912
+ const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
1913
+ return emailRegex.test(email);
1914
+ }
1915
+
1916
+ export function isNonEmptyString(val: any): boolean {
1917
+ return typeof val === 'string' && val.trim().length > 0;
1918
+ }
1919
+ `),r(B.default.join(o,"string.ts"),`export function capitalize(str: string): string {
1920
+ if (!str) return '';
1921
+ return str.charAt(0).toUpperCase() + str.slice(1);
1922
+ }
1923
+
1924
+ export function truncate(str: string, maxLength: number): string {
1925
+ if (!str || str.length <= maxLength) return str;
1926
+ return \`\${str.slice(0, maxLength)}...\`;
1927
+ }
1928
+ `),r(B.default.join(o,"index.ts"),`export * from './dateFormat';
1929
+ export * from './mobileNumber';
1930
+ export * from './currency';
1931
+ export * from './validation';
1932
+ export * from './string';
1933
+ `)}var K=x(require("path"));function we(e){let{targetDir:t}=e,o=K.default.join(t,"src","config");r(K.default.join(o,"env.ts"),`export const env = {
1934
+ API_BASE_URL: process.env.API_BASE_URL || 'https://api.example.com',
1935
+ APP_ENV: process.env.APP_ENV || 'development',
1936
+ IS_DEV: (process.env.APP_ENV || 'development') === 'development',
1937
+ };
1938
+
1939
+ export const GENERATOR_INFO = {
1940
+ name: 'create-mobile-app',
1941
+ version: '1.0.0',
1942
+ company: 'DevMinds Technologies',
1943
+ website: 'https://devminds.in',
1944
+ portfolio: 'https://devminds.in/portfolio',
1945
+ author: 'Dharmendra (Dev) kumar (https://www.linkedin.com/in/dharmendra-appdeveloper)',
1946
+ } as const;
1947
+ `),r(K.default.join(o,"index.ts"),`export * from './env';
1948
+ `)}var M=x(require("path"));function ke(e){let{targetDir:t}=e,o=M.default.join(t,"src","hooks");r(M.default.join(o,"useDebounce.ts"),`import { useState, useEffect } from 'react';
1949
+
1950
+ export function useDebounce<T>(value: T, delay = 500): T {
1951
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
1952
+
1953
+ useEffect(() => {
1954
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
1955
+ return () => clearTimeout(timer);
1956
+ }, [value, delay]);
1957
+
1958
+ return debouncedValue;
1959
+ }
1960
+ `),r(M.default.join(o,"useKeyboard.ts"),`import { useState, useEffect } from 'react';
1961
+ import { Keyboard, KeyboardEvent } from 'react-native';
1962
+
1963
+ export function useKeyboard() {
1964
+ const [isKeyboardVisible, setKeyboardVisible] = useState(false);
1965
+ const [keyboardHeight, setKeyboardHeight] = useState(0);
1966
+
1967
+ useEffect(() => {
1968
+ const showSub = Keyboard.addListener('keyboardDidShow', (e: KeyboardEvent) => {
1969
+ setKeyboardVisible(true);
1970
+ setKeyboardHeight(e.endCoordinates.height);
1971
+ });
1972
+ const hideSub = Keyboard.addListener('keyboardDidHide', () => {
1973
+ setKeyboardVisible(false);
1974
+ setKeyboardHeight(0);
1975
+ });
1976
+
1977
+ return () => {
1978
+ showSub.remove();
1979
+ hideSub.remove();
1980
+ };
1981
+ }, []);
1982
+
1983
+ return { isKeyboardVisible, keyboardHeight };
1984
+ }
1985
+ `),r(M.default.join(o,"useAppState.ts"),`import { useState, useEffect } from 'react';
1986
+ import { AppState, AppStateStatus } from 'react-native';
1987
+
1988
+ export function useAppState() {
1989
+ const [appState, setAppState] = useState<AppStateStatus>(AppState.currentState);
1990
+
1991
+ useEffect(() => {
1992
+ const sub = AppState.addEventListener('change', (nextState) => {
1993
+ setAppState(nextState);
1994
+ });
1995
+ return () => sub.remove();
1996
+ }, []);
1997
+
1998
+ return appState;
1999
+ }
2000
+ `),r(M.default.join(o,"index.ts"),`export * from './useDebounce';
2001
+ export * from './useKeyboard';
2002
+ export * from './useAppState';
2003
+ `)}var ee=x(require("path"));function Ae(e){let{targetDir:t}=e,o=ee.default.join(t,"src","types");r(ee.default.join(o,"index.ts"),`export interface CommonResponse<T = any> {
2004
+ success: boolean;
2005
+ data?: T;
2006
+ message?: string;
2007
+ error?: string;
2008
+ }
2009
+
2010
+ export type Nullable<T> = T | null;
2011
+ export type Optional<T> = T | undefined;
2012
+ `)}var Pe=x(require("path"));function De(e){let{targetDir:t,projectName:o}=e,n=`# ${o}
2013
+
2014
+ Create a production-ready React Native application foundation in one command.
2015
+
2016
+ ## Create
2017
+
2018
+ \`\`\`bash
2019
+ npx create-mobile-app ${o}
2020
+ \`\`\`
2021
+
2022
+ ## No questions
2023
+
2024
+ \`\`\`bash
2025
+ npx create-mobile-app ${o} --yes
2026
+ \`\`\`
2027
+
2028
+ ## Includes
2029
+
2030
+ \u2713 TypeScript
2031
+ \u2713 Responsive UI
2032
+ \u2713 Theme
2033
+ \u2713 Common Components
2034
+ \u2713 Stack
2035
+ \u2713 Bottom Tabs
2036
+ \u2713 Drawer
2037
+ \u2713 API Client
2038
+ \u2713 TanStack Query
2039
+ \u2713 Services
2040
+ \u2713 Authentication
2041
+ \u2713 Guest Mode
2042
+ \u2713 Storage
2043
+ \u2713 Constants
2044
+ \u2713 Utilities
2045
+ \u2713 Safe Area
2046
+
2047
+ ## Architecture
2048
+
2049
+ \`\`\`
2050
+ src/
2051
+ \u251C\u2500\u2500 api/ # Centralized HTTP API client with normalized responses and errors.
2052
+ \u251C\u2500\u2500 components/ # Reusable, theme-aware, responsive UI components (CText, CInput, CBox, etc.).
2053
+ \u251C\u2500\u2500 constant/ # Application layout, font, and spacing constants.
2054
+ \u251C\u2500\u2500 navigations/ # Screen names, Stack, Bottom 5 tabs, Drawer, and state-aware RootNavigator.
2055
+ \u251C\u2500\u2500 screen/ # Screen composition modules with styles and forms.
2056
+ \u251C\u2500\u2500 service/ # Clean API services exposing domain methods (Home, Profile, Auth).
2057
+ \u251C\u2500\u2500 theme/ # Light, Dark, and System theme provider and useTheme hook.
2058
+ \u251C\u2500\u2500 utils/ # Pure utilities for dates, phone formatting, currency, and validation.
2059
+ \u251C\u2500\u2500 auth/ # Backend-independent authentication context, session, and guest mode.
2060
+ \u251C\u2500\u2500 storage/ # Cross-platform persistent storage abstraction.
2061
+ \u2514\u2500\u2500 config/ # Environment setup and configuration constants.
2062
+ \`\`\`
2063
+
2064
+ ## Quick Examples
2065
+
2066
+ ### Component Usage
2067
+
2068
+ \`\`\`tsx
2069
+ import { CBox, CText, CButton } from '@/components';
2070
+
2071
+ export function Demo() {
2072
+ return (
2073
+ <CBox padding="md" gap="sm">
2074
+ <CText variant="title">Hello World</CText>
2075
+ <CButton title="Continue" onPress={() => console.log('Pressed')} />
2076
+ </CBox>
2077
+ );
2078
+ }
2079
+ \`\`\`
2080
+
2081
+ ### API & Service Data Flow
2082
+
2083
+ \`\`\`tsx
2084
+ // Service (src/service/home/index.ts)
2085
+ export const Home = {
2086
+ getHome: () => apiClient.get('/home'),
2087
+ };
2088
+
2089
+ // Query Hook (src/service/home/query.ts)
2090
+ export const useHomeQuery = () => useQuery({ queryKey: queryKeys.home.all, queryFn: Home.getHome });
2091
+
2092
+ // Screen (src/screen/Home/index.tsx)
2093
+ const { data, isLoading } = useHomeQuery();
2094
+ \`\`\`
2095
+
2096
+ ### Authentication
2097
+
2098
+ \`\`\`tsx
2099
+ import { useAuth } from '@/auth';
2100
+
2101
+ const { isAuthenticated, login, logout } = useAuth();
2102
+ \`\`\`
2103
+
2104
+ ### Navigation
2105
+
2106
+ Add a new screen constant in \`src/navigations/screenName/index.ts\` and register it in \`src/navigations/Stack/index.tsx\`.
2107
+
2108
+ ---
2109
+
2110
+ ## \u{1F468}\u200D\u{1F4BB} Built with create-mobile-app
2111
+
2112
+ Architecture powered by **DevMinds Technologies**
2113
+
2114
+ - \u{1F3E2} **Company**: **DevMinds Technologies** *(WEB \xB7 MOBILE \xB7 CLOUD \xB7 DESIGN)*
2115
+ - \u{1F310} **Website**: [devminds.in](https://devminds.in)
2116
+ - \u{1F4BC} **Portfolio**: [devminds.in/portfolio](https://devminds.in/portfolio)
2117
+ - \u{1F468}\u200D\u{1F4BB} **Lead Developer**: **Dharmendra (Dev) kumar**
2118
+ - \u{1F517} **LinkedIn**: [linkedin.com/in/dharmendra-appdeveloper](https://www.linkedin.com/in/dharmendra-appdeveloper)
2119
+ `;r(Pe.default.join(t,"README.md"),n)}var D=x(require("path")),te=x(require("fs"));function je(e){let{targetDir:t}=e,o=D.default.join(t,"assets");N(o),N(D.default.join(t,"src","assets","images")),N(D.default.join(t,"src","assets","fonts")),N(D.default.join(t,"src","assets","icons"));let n=[D.default.resolve(__dirname,"..","assets"),D.default.resolve(__dirname,"..","..","assets"),D.default.resolve(process.cwd(),"assets")],i={"icon.png":["icon.png"],"adaptive-icon.png":["adaptive-icon.png"],"splash.png":["splash.png","slpash.png"],"favicon.png":["favicon.png"]};for(let[a,c]of Object.entries(i)){let l=D.default.join(o,a),p=!1;for(let u of n){for(let h of c){let m=D.default.join(u,h);if(te.default.existsSync(m)){te.default.copyFileSync(m,l),p=!0;break}}if(p)break}}}var oe=require("child_process");async function Ne(e){let{targetDir:t,projectName:o,pm:n,skipInstall:i}=e;if(s.banner(),s.step(`Creating ${o}...`),console.log(""),e.platform==="expo"){let a=e.expoSdkTemplate||"blank@sdk-57";s.step(`Downloading official Expo template (${a})...`);try{(0,oe.execSync)(`npx -y create-expo-app@latest "${t}" --template ${a} --no-install`,{stdio:"inherit"})}catch{try{(0,oe.execSync)(`npx -y create-expo-app@latest "${t}" --template blank --no-install`,{stdio:"inherit"})}catch{N(t)}}}else N(t);return ge(e),je(e),s.success("Project foundation & Logo/Splash Assets (assets/)"),e.responsive&&s.success("Responsive system (react-native-dynamic-responsive)"),Te(e),s.success("Constants & variables"),we(e),ke(e),Ae(e),e.theme&&(be(e),s.success("Theme architecture (Light/Dark/System)")),e.components&&(ve(e),s.success("Common UI Components (CText, CBox, CInput, CButton, CDropdown)")),e.api&&(fe(e),s.success("API Client & Query key architecture")),e.service&&(ye(e),s.success("Service architecture (Home, Profile, Auth, User)")),e.storage&&(Se(e),s.success("Storage abstraction layer")),e.auth&&(Ce(e),s.success("Authentication & Guest Mode provider")),e.navigation&&(xe(e),s.success("Navigation (Stack, 5 Bottom Tabs, Drawer)")),he(e),s.success("Screen architecture (Home, Search, Notification, Profile, Settings, Login)"),e.husky&&s.success("Husky, Prettier & Lint-staged pre-commit error checks"),Fe(e),s.success("Utilities (Date, Mobile Number, Currency, Validation)"),De(e),s.success("README documentation"),console.log(""),i||(s.step("Installing dependencies..."),de(t,n)?s.success("Dependencies installed"):s.warn("Failed to install dependencies automatically. You can run install manually.")),s.completion(o,n),!0}async function Be(){parseInt(process.versions.node.split(".")[0],10)<20&&s.warn(`Node.js v${process.versions.node} detected. Node.js 20 or higher is required for Expo SDK 57+ and modern React Native architecture.`);let t=new $e.Command;t.name("create-mobile-app").description("Create a production-ready React Native + TypeScript mobile application foundation in one command.").version("1.0.0").argument("[project-name]","Name of the project directory to create").option("-y, --yes","Skip interactive questions and use complete app defaults").option("--platform <type>","Choose mobile platform: expo or cli").option("--sdk <version>","Choose Expo SDK version (default: ~57.0.0, e.g. 57, 58, 54, 52 or custom)").option("--responsive","Include react-native-dynamic-responsive system").option("--no-responsive","Disable responsive system").option("--theme","Include Theme architecture").option("--no-theme","Disable Theme architecture").option("--components","Include common UI components").option("--no-components","Disable common UI components").option("--navigation","Include Navigation architecture").option("--no-navigation","Disable Navigation architecture").option("--tabs","Include 5 Bottom Tabs").option("--no-tabs","Disable 5 Bottom Tabs").option("--drawer","Include Drawer navigation").option("--no-drawer","Disable Drawer navigation").option("--api","Include API client & Query key architecture").option("--no-api","Disable API client architecture").option("--query","Include TanStack Query setup").option("--no-query","Disable TanStack Query setup").option("--service","Include Service architecture").option("--no-service","Disable Service architecture").option("--auth","Include Authentication & session state").option("--no-auth","Disable Authentication").option("--guest","Include Guest mode").option("--no-guest","Disable Guest mode").option("--storage","Include Storage abstraction").option("--no-storage","Disable Storage abstraction").option("--husky","Include Husky, Prettier & Lint-staged pre-commit error checks").option("--no-husky","Disable Husky pre-commit hooks").option("--examples","Include example screens and demo data").option("--no-examples","Disable example screens").option("--pm <packageManager>","Package manager to use: npm, yarn, pnpm, or bun"),t.parse(process.argv);let o=t.opts(),[n]=t.args;try{let i=await ue(n,o);if(!i)return;await Ne(i)}catch(i){s.error(`Error generating project: ${i?.message||i}`),process.exit(1)}}Be();0&&(module.exports={runCli});