feat: Integrate Stripe payment processing and enhance admin settings management
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions

- Removed the previous demo payment system and implemented a real integration with Stripe for handling payments.
- Added new API routes for creating payment intents and handling webhooks from Stripe.
- Updated the database schema to include an Order model for storing payment details.
- Enhanced the admin page to manage pricing items, including the ability to upload 3D models.
- Introduced a settings management feature for the admin panel, allowing for dynamic key-value pairs.
- Improved the RobotModel component to support dynamic attire based on uploaded models.
- Added error handling and validation for file uploads and settings management.
This commit is contained in:
Najjar\NajjarV02
2026-04-14 10:14:18 +04:00
parent dc42aeb72a
commit 51671b85b8
13 changed files with 598 additions and 156 deletions
+16 -4
View File
@@ -5,15 +5,25 @@ import { useGLTF } from '@react-three/drei';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import { useConfigStore } from '@/store/useConfigStore';
import { personaStore, usePersonaStore } from '@/store/usePersonaStore';
const ATTIRE_GLB: Record<string, string> = {
const STATIC_ATTIRE_GLB: Record<string, string> = {
'emarati-kandura': '/Kandoura.glb',
'industrial-vest': '/Vest.glb',
'business-suit': '/Suit.glb',
};
// Preload all attire models so they're cached before user clicks
Object.values(ATTIRE_GLB).forEach((path) => useGLTF.preload(path));
// Preload static attire models so they're cached before user clicks
Object.values(STATIC_ATTIRE_GLB).forEach((p) => useGLTF.preload(p));
/** Merge static map with any custom GLBs stored in the persona store */
function buildAttireGlbMap(personas: { id: string; modelPath?: string }[]): Record<string, string> {
const dynamic: Record<string, string> = {};
personas.forEach((p) => {
if (p.modelPath && p.id !== 'none') dynamic[p.id] = p.modelPath;
});
return { ...dynamic, ...STATIC_ATTIRE_GLB }; // static takes priority
}
function easeInOutCubic(t: number): number {
return t < 0.5
@@ -78,6 +88,8 @@ export function RobotModel({ onError }: RobotModelProps) {
const activeColors = useConfigStore((state) => state.activeColors);
const activePersonaAttire = useConfigStore((state) => state.activePersonaAttire);
// Subscribe to persona store so custom GLB paths are reactive
const personas = usePersonaStore((s) => s.personas);
// Detect attire change and trigger spin
useEffect(() => {
@@ -174,7 +186,7 @@ export function RobotModel({ onError }: RobotModelProps) {
});
}, [activeColors]);
const attireGlbPath = ATTIRE_GLB[displayedAttire] || null;
const attireGlbPath = buildAttireGlbMap(personas)[displayedAttire] || null;
const handleAttireLoaded = () => {
setAttireReady(true);