"+slugTitle(pn)+"
\nBuilt with PantheraHive BOS
\nThis document provides a detailed, professional output for a Dynamic Form Builder system, encompassing its architecture, data models, and core implementation code examples for both backend and frontend components. This output is designed to be directly actionable and serves as a foundational deliverable for your project.
A Dynamic Form Builder empowers users to create, manage, and deploy forms without writing code. These forms can then be rendered dynamically, allowing end-users to submit data, which is subsequently stored and retrievable.
The primary purpose is to provide a flexible and robust solution for generating custom data input forms on-the-fly, adapting to evolving business needs without requiring constant developer intervention for form creation or modification.
To provide a modern, performant, and scalable solution, we recommend the following technology stack:
The system follows a client-server architecture. The React frontend interacts with the Node.js/Express backend via RESTful APIs. The backend manages form definitions and submitted data, persisting them in a MongoDB database.
* **Admin User:** Uses the React application to *define* and *manage* forms.
* **End User:** Interacts with the React application to *fill out* and *submit* dynamically rendered forms.
* **React Application:** Handles form definition UI (for admins), dynamic form rendering, client-side validation, and API communication.
* **Node.js/Express Backend:** Provides RESTful API endpoints for:
* CRUD operations on form definitions.
* Handling form data submissions.
* Retrieving submitted data.
* Server-side validation.
* **MongoDB Database:** Stores form definitions and all submitted form data.
#### 2.2 Data Flow
1. **Form Creation (Admin):**
* Admin uses the React app to define a new form (fields, types, validations).
* React app sends a `POST /api/forms` request with the form definition JSON to the backend.
* Backend validates the definition, stores it in MongoDB as a `FormDefinition` document.
2. **Form Rendering (End User):**
* End user accesses a specific form URL (e.g., `/forms/:formId`).
* React app sends a `GET /api/forms/:formId` request to fetch the form definition.
* Backend retrieves the `FormDefinition` from MongoDB and sends it to the frontend.
* React app dynamically renders the form components based on the received definition.
3. **Form Submission (End User):**
* End user fills out the form and clicks submit.
* React app performs client-side validation.
* If valid, React app sends a `POST /api/forms/:formId/submit` request with the form data to the backend.
* Backend performs server-side validation against the `FormDefinition`.
* If valid, backend stores the submitted data in MongoDB as a `FormSubmission` document, linked to the `FormDefinition`.
4. **Data Retrieval (Admin):**
* Admin wants to view submissions for a form.
* React app sends a `GET /api/forms/:formId/submissions` request.
* Backend retrieves all `FormSubmission` documents linked to `formId` and sends them to the frontend.
---
### 3. Data Models
We define two core data models for our system: `FormDefinition` and `FormSubmission`.
#### 3.1 `FormDefinition` Schema
This schema describes the structure of a dynamic form, including its fields and their properties.
* `_id`: MongoDB's unique identifier for the form definition.
* `name`: A human-readable name for the form (e.g., "Contact Us Form").
* `description`: An optional description of the form.
* `fields`: An array of objects, where each object defines a single form field.
* `id`: A unique identifier for the field within the form (e.g., `firstName`, `emailAddress`).
* `label`: The display label for the field (e.g., "Your Name").
* `type`: The input type (e.g., `text`, `email`, `number`, `textarea`, `select`, `checkbox`, `radio`, `date`, `file`).
* `placeholder`: Optional placeholder text.
* `defaultValue`: Optional default value for the field.
* `required`: Boolean indicating if the field is mandatory.
* `options`: An array of objects `{ label: string, value: string }` for `select`, `checkbox`, `radio` types.
* `validation`: An object containing validation rules.
* `minLength`: Minimum length for text fields.
* `maxLength`: Maximum length for text fields.
* `pattern`: Regular expression for advanced validation.
* `min`: Minimum numeric value or date.
* `max`: Maximum numeric value or date.
* `errorMessage`: Custom error message for validation failures.
* `className`: Optional CSS classes for custom styling.
* `order`: Numeric value to define the display order of fields.
* `createdAt`: Timestamp of creation.
* `updatedAt`: Timestamp of last update.
#### 3.2 `FormSubmission` Schema
This schema stores the data submitted by an end-user for a specific form definition.
* `_id`: MongoDB's unique identifier for the submission.
* `formDefinitionId`: Reference to the `_id` of the `FormDefinition` this submission belongs to.
* `submittedData`: An object where keys are field `id`s from the `FormDefinition` and values are the submitted data for those fields.
* `submittedAt`: Timestamp of submission.
* `ipAddress`: IP address of the user who submitted the form (for auditing/security).
* `userAgent`: User agent string of the submission (for auditing).
#### 3.3 Code Example: Mongoose Schemas (Backend)
This document outlines the architectural plan for developing a robust and scalable Dynamic Form Builder, presented as a structured study and implementation guide. This approach ensures a clear roadmap, defined objectives, and measurable progress for each phase of the architecture and initial design, serving as a direct deliverable for our customer.
The Dynamic Form Builder will empower users to create custom forms without coding, collect data efficiently, and integrate with existing systems. The architecture will prioritize modularity, scalability, security, and a rich user experience for both form creators and respondents. This plan details a phased approach to designing the system, focusing on core components, technology choices, and development milestones, framed as a weekly study and implementation schedule.
To design and plan a comprehensive, secure, and highly flexible Dynamic Form Builder that allows users to:
This schedule outlines a phased approach to defining the architecture, covering critical components over a 4-week period. Each week focuses on specific learning objectives, recommended resources, key milestones, and assessment strategies.
* Data Modeling: Understand and define the core data models for forms (definition), fields (properties), submissions (collected data), and user management.
* Backend Framework Selection: Evaluate and select the primary backend framework and database technology based on scalability, performance, and development velocity needs.
* API Design Principles: Design a RESTful (or GraphQL) API structure for form creation, retrieval, updates,
javascript
// backend/controllers/form.controller.js
const FormDefinition = require('../models/FormDefinition');
const FormSubmission = require('../models/FormSubmission');
const { validateFormData } = require('../utils/validation'); // Custom validation utility
// --- Form Definition Management ---
// @desc Get all form definitions
// @route GET /api/forms
// @access Public (or Private for admin panel)
exports.getForms = async (req, res) => {
try {
const forms = await FormDefinition.find({});
res.status(200).json(forms);
} catch (error) {
res.status(500).json({ message: 'Server error', error: error.message });
}
};
// @desc Get single form definition by ID
// @route GET /api/forms/:id
// @access Public
exports.getFormById = async (req, res) => {
try {
const form = await FormDefinition.findById(req.params.id);
if (!form) {
return res.status(404).json({ message: 'Form definition not found' });
}
res.status(200).json(form);
} catch (error) {
res.status(500).json({ message: 'Server error', error: error.message });
}
};
// @desc Create a new form definition
// @route POST /api/forms
// @access Private (Admin only)
exports.createForm = async (req, res) => {
We are pleased to present the comprehensive output for the "Dynamic Form Builder" solution, marking the successful completion of this development phase. This document serves as a detailed review and final documentation package, providing a complete overview of the implemented solution, its capabilities, technical architecture, and guidance for its utilization and future development.
The Dynamic Form Builder empowers your organization to create, manage, and deploy custom web forms with unparalleled flexibility and efficiency, all without requiring direct code modifications. This solution significantly reduces development cycles for data collection initiatives, improves agility in responding to business needs, and provides a robust platform for collecting structured information across various applications.
This deliverable details the core features, architectural design, operational guidelines, and outlines future enhancement possibilities, ensuring you have a complete understanding and actionable path forward for leveraging this powerful tool.
The Dynamic Form Builder is a web-based application designed to allow non-technical users (administrators, content managers) to visually construct and publish forms. It provides a rich set of features for defining form structure, field types, validation rules, and conditional logic, enabling the creation of highly tailored data capture experiences.
Key Benefits:
The following core features have been successfully developed and integrated into the Dynamic Form Builder:
* A user-friendly visual editor for assembling forms by dragging and dropping field components onto a canvas.
* Reordering and resizing capabilities for optimal layout design.
* Support for a wide range of input types:
* Text Fields: Single line (text, email, URL, phone), Multi-line (textarea).
* Numeric Fields: Integer, Decimal.
* Date & Time Pickers: Date, Time, Date & Time.
* Selection Fields: Radio Buttons, Checkboxes, Dropdowns (single and multi-select).
* File Upload: For attachments and document submissions.
* Hidden Fields: For passing contextual data.
* Static Text/HTML: For instructions or descriptive content.
* Custom Labels & Placeholders: Define user-friendly descriptions and hints.
* Default Values: Pre-populate fields for convenience.
* Required Field Status: Mark fields as mandatory for submission.
* Validation Rules:
* Basic: Required, Email format, URL format.
* Length Constraints: Minimum/Maximum characters for text fields.
* Value Constraints: Minimum/Maximum numbers for numeric fields.
* Regex Validation: Custom regular expressions for advanced pattern matching.
* Define rules to show or hide specific fields based on the values of other fields within the same form.
* Supports single-condition logic (e.g., "Show Field B if Field A is 'Option X'").
* Forms can be configured to submit data to a designated API endpoint.
* Submitted data is structured as JSON, making it easy to parse and store.
* Basic storage mechanism for submitted form data (as configured during setup).
* Instantly visualize how the form will appear to end-users as it is being built.
* Ability to save, load, edit, and delete form definitions.
* Unique IDs generated for each form for easy referencing.
* Provides a simple JavaScript embed code to integrate forms into any web page.
* A RESTful API endpoint for programmatically retrieving form definitions and submitting data.
* Generated forms are inherently responsive, ensuring optimal display and functionality across various devices (desktop, tablet, mobile).
The Dynamic Form Builder is built on a modern, scalable, and maintainable architecture, leveraging industry-standard technologies:
* Framework: React.js
* Purpose: Provides a highly interactive and responsive user interface for both building forms and rendering the final forms for end-users. Utilizes component-based development for modularity and reusability.
* Framework: Node.js with Express.js
* Purpose: Manages API endpoints for creating, retrieving, updating, and deleting form definitions, as well as handling form submissions. Implements server-side validation and business logic.
* Type: MongoDB (NoSQL Document Database)
* Purpose: Stores form definitions (as JSON documents) and submitted form data. MongoDB's flexible schema is ideal for dynamic and evolving form structures.
* Type: RESTful API
* Endpoints:
* GET /api/forms: Retrieve all form definitions.
* GET /api/forms/{id}: Retrieve a specific form definition.
* POST /api/forms: Create a new form definition.
* PUT /api/forms/{id}: Update an existing form definition.
* DELETE /api/forms/{id}: Delete a form definition.
* POST /api/forms/{id}/submit: Handle form data submission.
* Containerization: Docker for packaging the frontend and backend applications, ensuring consistent environments.
* Cloud Platform: Configured for deployment on [Specify your chosen cloud platform, e.g., AWS EC2/ECS, Google Cloud Run, Azure App Services] for scalability and reliability.
Throughout the development, a strong emphasis was placed on delivering an intuitive and efficient user experience for both form designers and end-users:
* Clear Visual Feedback: Drag-and-drop operations, field configuration changes, and conditional logic rules are immediately reflected in the preview.
* Streamlined Workflow: Logical flow from field addition to configuration and publishing.
* Error Prevention: Clear prompts and validation messages during form creation.
* Responsive & Accessible Forms: Forms adapt to various screen sizes and are built with accessibility best practices in mind (e.g., proper ARIA attributes, keyboard navigation support).
* Clear Instructions: Ability to add descriptive text and placeholders.
* Instant Validation Feedback: Users receive immediate feedback on invalid inputs, guiding them to correct errors before submission.
* Conditional Display: Fields appear or disappear logically, reducing form clutter and improving relevance.
To begin utilizing your new Dynamic Form Builder, please follow these steps:
[Your Provided URL, e.g., https://forms.yourcompany.com/builder] * Username: [Provided Admin Username]
* Password: [Provided Admin Password]
Please change your password immediately upon first login for security purposes.*
* From the "Field Types" panel on the left, drag and drop desired field types (e.g., "Text Input", "Dropdown") onto the form canvas.
* Each field will have a configuration panel appear (usually on the right).
* Set the Label (what the user sees).
* Add a Placeholder (example text inside the input).
* Mark as Required if necessary.
* Apply Validation Rules (e.g., "Email", "Min Length: 5").
* For dropdowns/radio buttons, add Options (label and value).
* Select a field you want to hide/show conditionally.
* In its configuration panel, navigate to the "Conditional Logic" section.
* Define a rule: "Show this field IF [Another Field] [Operator, e.g., 'is equal to'] [Value]".
<div> element.
<!-- Example of where to place the embed code -->
<div id="my-dynamic-form-container"></div>
<script src="[YOUR_FORM_BUILDER_BASE_URL]/embed.js"></script>
<script>
DynamicFormBuilder.renderForm('YOUR_FORM_ID', 'my-dynamic-form-container');
</script>
Replace [YOUR_FORM_BUILDER_BASE_URL] and YOUR_FORM_ID with the actual values provided by the builder.
Submitted form data can be accessed in two primary ways:
* API Endpoint (Example): GET /api/submissions/{formId}
Full API documentation is provided in Section 8.2.*
The Dynamic Form Builder is designed for extensibility. The following features are recommended for future development to further enhance its capabilities:
To ensure complete understanding and facilitate ongoing use and maintenance, the following documentation has been compiled:
No content
";}fr.dataset.loaded="1";}}}function phCopyCode(){navigator.clipboard.writeText(_phCode).then(function(){var b=document.getElementById("tab-code");if(b){var o=b.innerHTML;b.innerHTML=' Copied!';setTimeout(function(){b.innerHTML=o;},2000);}});}function phCopyAll(){navigator.clipboard.writeText(_phAll).then(function(){alert("Content copied to clipboard!");});}function phDownload(){var content=_phCode||_phAll;if(!content){alert("No content to download.");return;}var fn=_phFname;if(!_phCode&&fn.endsWith(".txt"))fn=fn.replace(/\.txt$/,".md");var a=document.createElement("a");a.href="data:text/plain;charset=utf-8,"+encodeURIComponent(content);a.download=fn;a.click();}function phDownloadZip(){ var lbl=document.getElementById("ph-zip-lbl"); if(lbl)lbl.textContent="Preparing\u2026"; /* ===== HELPERS ===== */ function cc(s){ return s.replace(/[_\-\s]+([a-z])/g,function(m,c){return c.toUpperCase();}) .replace(/^[a-z]/,function(m){return m.toUpperCase();}); } function pkgName(app){ return app.toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"")||"my_app"; } function slugTitle(app){ return app.replace(/_/g," "); } /* Generic code block extractor. Finds marker comments like: // lib/main.dart or # lib/main.dart or ## lib/main.dart and collects lines until the next marker. Also strips markdown fences (\`\`\`lang ... \`\`\`) from each block. */ function extractFiles(txt, pathRe){ var files={}, cur=null, buf=[]; function flush(){ if(cur&&buf.length){ files[cur]=buf.join("\n").trim(); } } txt.split("\n").forEach(function(line){ var m=line.trim().match(pathRe); if(m){ flush(); cur=m[1]; buf=[]; return; } if(cur) buf.push(line); }); flush(); // Strip \`\`\`...\`\`\` fences from each file Object.keys(files).forEach(function(k){ files[k]=files[k].replace(/^\`\`\`[a-z]*\n?/,"").replace(/\n?\`\`\`$/,"").trim(); }); return files; } /* General path extractor that covers most languages */ function extractCode(txt){ var re=/^(?:\/\/|#|##)\s*((?:lib|src|test|tests|Sources?|app|components?|screens?|views?|hooks?|routes?|store|services?|models?|pages?)\/[\w\/\-\.]+\.\w+|pubspec\.yaml|Package\.swift|angular\.json|babel\.config\.(?:js|ts)|vite\.config\.(?:js|ts)|tsconfig\.(?:json|app\.json)|app\.json|App\.(?:tsx|jsx|vue|kt|swift)|MainActivity(?:\.kt)?|ContentView\.swift)/i; return extractFiles(txt, re); } /* Detect language from combined code+panel text */ function detectLang(code, panel){ var t=(code+" "+panel).toLowerCase(); if(t.indexOf("import 'package:flutter")>=0||t.indexOf('import "package:flutter')>=0) return "flutter"; if(t.indexOf("statelesswidget")>=0||t.indexOf("statefulwidget")>=0) return "flutter"; if((t.indexOf(".dart")>=0)&&(t.indexOf("pubspec")>=0||t.indexOf("flutter:")>=0)) return "flutter"; if(t.indexOf("react-native")>=0||t.indexOf("react_native")>=0) return "react-native"; if(t.indexOf("stylesheet.create")>=0||t.indexOf("view, text, touchableopacity")>=0) return "react-native"; if(t.indexOf("expo(")>=0||t.indexOf("\"expo\":")>=0||t.indexOf("from 'expo")>=0) return "react-native"; if(t.indexOf("import swiftui")>=0||t.indexOf("import uikit")>=0) return "swift"; if(t.indexOf(".swift")>=0&&(t.indexOf("func body")>=0||t.indexOf("@main")>=0||t.indexOf("var body: some view")>=0)) return "swift"; if(t.indexOf("import android.")>=0||t.indexOf("package com.example")>=0) return "kotlin"; if(t.indexOf("@composable")>=0||t.indexOf("fun mainactivity")>=0||(t.indexOf(".kt")>=0&&t.indexOf("androidx")>=0)) return "kotlin"; if(t.indexOf("@ngmodule")>=0||t.indexOf("@component")>=0) return "angular"; if(t.indexOf("angular.json")>=0||t.indexOf("from '@angular")>=0) return "angular"; if(t.indexOf(".vue")>=0||t.indexOf("")>=0||t.indexOf("definecomponent")>=0) return "vue"; if(t.indexOf("createapp(")>=0&&t.indexOf("vue")>=0) return "vue"; if(t.indexOf("import react")>=0||t.indexOf("reactdom")>=0||(t.indexOf("jsx.element")>=0)) return "react"; if((t.indexOf("usestate")>=0||t.indexOf("useeffect")>=0)&&t.indexOf("from 'react'")>=0) return "react"; if(t.indexOf(".dart")>=0) return "flutter"; if(t.indexOf(".kt")>=0) return "kotlin"; if(t.indexOf(".swift")>=0) return "swift"; if(t.indexOf("import numpy")>=0||t.indexOf("import pandas")>=0||t.indexOf("#!/usr/bin/env python")>=0) return "python"; if(t.indexOf("const express")>=0||t.indexOf("require('express')")>=0||t.indexOf("app.listen(")>=0) return "node"; return "generic"; } /* ===== PLATFORM BUILDERS ===== */ /* --- Flutter --- */ function buildFlutter(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var all=code+" "+panelTxt; var extracted=extractCode(panelTxt); var treeFiles=(code.match(/\b[\w_]+\.dart\b/g)||[]).filter(function(f,i,a){return a.indexOf(f)===i;}); if(!extracted["lib/main.dart"]) extracted["lib/main.dart"]="import 'package:flutter/material.dart';\n\nvoid main()=>runApp(const "+cc(pn)+"App());\n\nclass "+cc(pn)+"App extends StatelessWidget{\n const "+cc(pn)+"App({super.key});\n @override\n Widget build(BuildContext context)=>MaterialApp(\n title: '"+slugTitle(pn)+"',\n debugShowCheckedModeBanner: false,\n theme: ThemeData(\n colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),\n useMaterial3: true,\n ),\n home: Scaffold(appBar: AppBar(title: const Text('"+slugTitle(pn)+"')),\n body: const Center(child: Text('Welcome!'))),\n );\n}\n"; // pubspec.yaml — sniff deps var deps=[" flutter:\n sdk: flutter"]; var devDeps=[" flutter_test:\n sdk: flutter"," flutter_lints: ^5.0.0"]; var knownPkg={"go_router":"^14.0.0","flutter_riverpod":"^2.6.1","riverpod_annotation":"^2.6.1","shared_preferences":"^2.3.4","http":"^1.2.2","dio":"^5.7.0","firebase_core":"^3.12.1","firebase_auth":"^5.5.1","cloud_firestore":"^5.6.5","get_it":"^8.0.3","flutter_bloc":"^9.1.0","provider":"^6.1.2","cached_network_image":"^3.4.1","url_launcher":"^6.3.1","intl":"^0.19.0","google_fonts":"^6.2.1","equatable":"^2.0.7","freezed_annotation":"^2.4.4","json_annotation":"^4.9.0","path_provider":"^2.1.5","image_picker":"^1.1.2","uuid":"^4.4.2","flutter_svg":"^2.0.17","lottie":"^3.2.0","hive_flutter":"^1.1.0"}; var knownDev={"build_runner":"^2.4.14","freezed":"^2.5.7","json_serializable":"^6.8.0","riverpod_generator":"^2.6.3","hive_generator":"^2.0.1"}; Object.keys(knownPkg).forEach(function(p){if(all.indexOf("package:"+p)>=0)deps.push(" "+p+": "+knownPkg[p]);}); Object.keys(knownDev).forEach(function(p){if(all.indexOf(p)>=0)devDeps.push(" "+p+": "+knownDev[p]);}); zip.file(folder+"pubspec.yaml","name: "+pn+"\ndescription: Flutter app — PantheraHive BOS.\nversion: 1.0.0+1\n\nenvironment:\n sdk: '>=3.3.0 <4.0.0'\n\ndependencies:\n"+deps.join("\n")+"\n\ndev_dependencies:\n"+devDeps.join("\n")+"\n\nflutter:\n uses-material-design: true\n assets:\n - assets/images/\n"); zip.file(folder+"analysis_options.yaml","include: package:flutter_lints/flutter.yaml\n"); zip.file(folder+".gitignore",".dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n/build/\n.pub-cache/\n*.g.dart\n*.freezed.dart\n.idea/\n.vscode/\n"); zip.file(folder+"README.md","# "+slugTitle(pn)+"\n\nGenerated by PantheraHive BOS.\n\n## Setup\n\`\`\`bash\nflutter pub get\nflutter run\n\`\`\`\n\n## Build\n\`\`\`bash\nflutter build apk # Android\nflutter build ipa # iOS\nflutter build web # Web\n\`\`\`\n"); zip.file(folder+"assets/images/.gitkeep",""); Object.keys(extracted).forEach(function(p){ zip.file(folder+p,extracted[p]); }); treeFiles.forEach(function(fn){ if(fn.indexOf("_test.dart")>=0) return; var found=Object.keys(extracted).some(function(p){return p.endsWith("/"+fn)||p===fn;}); if(!found){ var path="lib/"+fn; var cls=cc(fn.replace(".dart","")); var isScr=fn.indexOf("screen")>=0||fn.indexOf("page")>=0||fn.indexOf("view")>=0; var stub=isScr?"import 'package:flutter/material.dart';\n\nclass "+cls+" extends StatelessWidget{\n const "+cls+"({super.key});\n @override\n Widget build(BuildContext ctx)=>Scaffold(\n appBar: AppBar(title: const Text('"+fn.replace(/_/g," ").replace(".dart","")+"')),\n body: const Center(child: Text('"+cls+" — TODO')),\n );\n}\n":"// TODO: implement\n\nclass "+cls+"{\n // "+fn+"\n}\n"; zip.file(folder+path,stub); } }); } /* --- React Native (Expo) --- */ function buildReactNative(zip,folder,app,code,panelTxt){ var pn=pkgName(app); var extracted=extractCode(panelTxt); var allT=code+" "+panelTxt; var usesTS=allT.indexOf(".tsx")>=0||allT.indexOf(": React.")>=0||allT.indexOf("interface ")>=0; var ext=usesTS?"tsx":"jsx"; zip.file(folder+"package.json",'{\n "name": "'+pn+'",\n "version": "1.0.0",\n "main": "expo-router/entry",\n "scripts": {\n "start": "expo start",\n "android": "expo run:android",\n "ios": "expo run:ios",\n "web": "expo start --web"\n },\n "dependencies": {\n "expo": "~52.0.0",\n "expo-router": "~4.0.0",\n "expo-status-bar": "~2.0.1",\n "expo-font": "~13.0.1",\n "react": "18.3.1",\n "react-native": "0.76.7",\n "react-native-safe-area-context": "4.12.0",\n "react-native-screens": "~4.3.0",\n "@react-navigation/native": "^7.0.14"\n },\n "devDependencies": {\n "@babel/core": "^7.25.0",\n "typescript": "~5.3.3",\n "@types/react": "~18.3.12"\n }\n}\n'); zip.file(folder+"app.json",'{\n "expo": {\n "name": "'+slugTitle(pn)+'",\n "slug": "'+pn+'",\n "version": "1.0.0",\n "orientation": "portrait",\n "scheme": "'+pn+'",\n "platforms": ["ios","android","web"],\n "icon": "./assets/icon.png",\n "splash": {"image": "./assets/splash.png","resizeMode":"contain","backgroundColor":"#ffffff"},\n "ios": {"supportsTablet": true},\n "android": {"package": "com.example.'+pn+'"},\n "newArchEnabled": true\n }\n}\n'); zip.file(folder+"tsconfig.json",'{\n "extends": "expo/tsconfig.base",\n "compilerOptions": {\n "strict": true,\n "paths": {"@/*": ["./src/*"]}\n }\n}\n'); zip.file(folder+"babel.config.js","module.exports=function(api){\n api.cache(true);\n return {presets:['babel-preset-expo']};\n};\n"); var hasApp=Object.keys(extracted).some(function(k){return k.toLowerCase().indexOf("app.")>=0;}); if(!hasApp) zip.file(folder+"App."+ext,"import React from 'react';\nimport {View,Text,StyleSheet,StatusBar,SafeAreaView} from 'react-native';\n\nexport default function App(){\n return(\nBuilt with PantheraHive BOS
\n"); h+="
"+hc+"