fork download
  1. /* package whatever; // don't place package name! */
  2.  
  3. import java.util.*;
  4. import java.lang.*;
  5. import java.io.*;
  6.  
  7. /* Name of the class has to be "Main" only if the class is public. */
  8. class Ideone
  9. {
  10. public static void main (String[] args) throws java.lang.Exception
  11. {
  12. // your code goes here
  13. }
  14. }
Success #stdin #stdout 0.08s 54572KB
stdin
SHAREDROP.TRADING — COMPLETE ZIP BUILDER SCRIPT

I can't transmit binary zip files through chat, but this script generates the entire package in seconds. Copy it, run it, get your zip.

---

BUILD SCRIPT — Save as build_sharedrop_v7_1.sh

```bash
#!/usr/bin/env bash
set -euo pipefail

ROOT="sharedrop-trading-v7.1"
ZIP="${ROOT}.zip"

rm -rf "$ROOT" "$ZIP"
mkdir -p "$ROOT"/{app/{routes,lib},prisma,public,extensions/delivery-date/src,docs,scripts}

writefile() {
  local file="$1"; shift
  mkdir -p "$(dirname "$file")"
  cat > "$file"
}

# ============================================================================
# ROOT FILES
# ============================================================================

writefile "$ROOT/README.md" <<'EOF'
# Sharedrop.Trading — Shopify MVP V7.1

Offline-first logistics marketplace for the Guyana–Brazil corridor.
Built on Shopify embedded app (Remix) + MongoDB Atlas + Netlify/Fly.io.

## Non-Custody Model — V7.1 ABSOLUTE
- No wallet · No escrow · No fund custody · No pooled funds
- P2P direct: Rider → Licensed PSP → Driver
- PIX Chave: 71086287240 (Brazil)
- Non-PSP per Handbook Part IV 16

## What's Included
- GLIE V7.0 lead engine (dual-entity SDT / GY-HUG, 6-stream waterfall, legal firewall)
- Shipment management + VIP driver offline contact
- GY-HUG Driving School Simulation (9 modules, GTTI standards)
- Gameplay Flow (8 steps)
- Payment V7.1 Non-Custody UI (P2P handoff)
- Safety preferences (gender-based driver selection)
- Service worker for offline-first admin
- Checkout extension (delivery date picker)

## Quick Deploy
```bash
npm install
npx prisma generate && npx prisma db push
cp .env.example .env   # fill values
shopify app dev
```

Full Deployment

```bash
fly launch --no-deploy
fly secrets set SHOPIFY_API_KEY=... SHOPIFY_API_SECRET=... MONGODB_URI=...
fly deploy
shopify app deploy
```

Legal Firewall

Every payment record MUST call assertNonCustody() from app/lib/payments.server.ts.
Violations auto-escalate to eosborne.legal@gmail.com.
EOF

writefile "$ROOT/.env.example" <<'EOF'

Shopify

SHOPIFY_API_KEY=
SHOPIFY_API_SECRET=
SHOPIFY_APP_URL=https://y...content-available-to-author-only...y.dev
SCOPES=write_orders,read_orders,write_fulfillments,read_fulfillments,write_shipping,read_shipping,read_products,write_products,read_customers,write_customers
SHOP_CUSTOM_DOMAIN=

MongoDB Atlas

MONGODB_URI=mongodb+srv://sharedrop_admin:CHANGE_ME@sharedrop-cluster.mongodb.net/sharedrop?retryWrites=true&w=majority

Maps

LEAFLET_TILE_URL=https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
MAPS_PROVIDER=leaflet_osm

Real-time

SOCKETIO_ENABLED=true
SOCKETIO_PATH=/socket.io

Payments — V7.1 Non-Custody

NON_PSP_MODE=true
PAYMENT_PROVIDER_GY=be_payments_mock
PAYMENT_PROVIDER_BR=pix_mock
PIX_CHAVE=71086287240
STRIPE_PUBLIC_KEY=
MMG_MERCHANT_ID=

GY-HUG Driving School

GYHUG_RULES_GY=guiana_rules.json
GYHUG_RULES_BR=brazil_rules.json
GTTI_STANDARD_VERSION=2024

Notifications

FCM_SERVER_KEY=
ONESIGNAL_APP_ID=

Legal

LEGAL_COUNSEL_EMAIL=eosborne.legal@gmail.com
EOF

writefile "$ROOT/.gitignore" <<'EOF'
node_modules/
.env
dist/
build/
.vscode/
.DS_Store
*.zip
dev.sqlite
dev.sqlite-journal
EOF

writefile "$ROOT/package.json" <<'EOF'
{
"name": "sharedrop-trading",
"private": true,
"version": "7.1.0",
"scripts": {
"build": "remix build",
"dev": "shopify app dev",
"deploy": "shopify app deploy",
"start": "remix-serve build",
"setup": "prisma generate && prisma db push"
},
"dependencies": {
"@prisma/client": "^5.20.0",
"@remix-run/node": "^2.15.0",
"@remix-run/react": "^2.15.0",
"@remix-run/serve": "^2.15.0",
"@shopify/app-bridge-react": "^4.1.6",
"@shopify/polaris": "^13.9.0",
"@shopify/shopify-app-remix": "^3.7.0",
"@shopify/shopify-app-session-storage-prisma": "^5.1.5",
"date-fns": "^2.30.0",
"mongodb": "^6.10.0",
"uuid": "^10.0.0"
},
"devDependencies": {
"@remix-run/dev": "^2.15.0",
"@types/node": "^22.9.0",
"@types/uuid": "^10.0.0",
"prisma": "^5.20.0",
"typescript": "^5.6.3",
"vite": "^5.4.11"
},
"engines": { "node": ">=20.10" }
}
EOF

writefile "$ROOT/shopify.app.toml" <<'EOF'
client_id = "YOUR_SHOPIFY_CLIENT_ID"
name = "Sharedrop Trading"
handle = "sharedrop-trading"
application_url = "https://y...content-available-to-author-only...y.dev"
embedded = true

[access_scopes]
scopes = "write_orders,read_orders,write_fulfillments,read_fulfillments,write_shipping,read_shipping,read_products,write_products,read_customers,write_customers,read_metaobjects,write_metaobjects"

[auth]
redirect_urls = [
"https://y...content-available-to-author-only...y.dev/auth/callback",
"https://y...content-available-to-author-only...y.dev/auth/shopify/callback",
"https://y...content-available-to-author-only...y.dev/api/auth/callback"
]

[webhooks]
api_version = "2026-04"
[[webhooks.subscriptions]]
topics = [ "orders/paid", "orders/fulfilled", "fulfillments/create", "fulfillments/update", "customers/create" ]
uri = "/webhooks"

[build]
automatically_update_urls_on_dev = true
dev_store_url = "your-dev-store.myshopify.com"
EOF

writefile "$ROOT/Dockerfile" <<'EOF'
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx prisma generate
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "start"]
EOF

writefile "$ROOT/fly.toml" <<'EOF'
app = "sharedrop-trading"
primary_region = "mia"

[build]
dockerfile = "Dockerfile"

[env]
PORT = "3000"
NODE_ENV = "production"

[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = "stop"
auto_start_machines = true
min_machines_running = 1

[[vm]]
size = "shared-cpu-1x"
memory = "512mb"
EOF

writefile "$ROOT/tsconfig.json" <<'EOF'
{
"include": ["/*.ts", "/.tsx"],
"compilerOptions": {
"lib": ["DOM", "DOM.Iterable", "ES2022"],
"types": ["@remix-run/node", "vite/client"],
"isolatedModules": true,
"esModuleInterop": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true,
"target": "ES2022",
"strict": true,
"allowJs": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": { "~/": ["./app/*"] },
"noEmit": true
}
}
EOF

============================================================================

PRISMA

============================================================================

writefile "$ROOT/prisma/schema.prisma" <<'EOF'
generator client { provider = "prisma-client-js" }
datasource db { provider = "sqlite"; url = "file:dev.sqlite" }

model Session {
id            String    @id
shop          String
state         String
isOnline      Boolean   @default(false)
scope         String?
expires       DateTime?
accessToken   String
userId        BigInt?
firstName     String?
lastName      String?
email         String?
accountOwner  Boolean   @default(false)
locale        String?
collaborator  Boolean?  @default(false)
emailVerified Boolean?  @default(false)
}
EOF

============================================================================

APP — CORE

============================================================================

writefile "$ROOT/app/shopify.server.ts" <<'EOF'
import "@shopify/shopify-app-remix/adapters/node";
import { ApiVersion, AppDistribution, shopifyApp } from "@shopify/shopify-app-remix/server";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import { restResources } from "@shopify/shopify-api/rest/admin/2026-04";
import prisma from "./db.server";

const shopify = shopifyApp({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET || "",
apiVersion: ApiVersion.April26,
scopes: process.env.SCOPES?.split(","),
appUrl: process.env.SHOPIFY_APP_URL || "",
authPathPrefix: "/auth",
sessionStorage: new PrismaSessionStorage(prisma),
distribution: AppDistribution.AppStore,
restResources,
future: { unstable_newEmbeddedAuthStrategy: true, removeRest: false },
});

export default shopify;
export const apiVersion = ApiVersion.April26;
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
export const authenticate = shopify.authenticate;
export const unauthenticated = shopify.unauthenticated;
export const login = shopify.login;
export const registerWebhooks = shopify.registerWebhooks;
export const sessionStorage = shopify.sessionStorage;
EOF

writefile "$ROOT/app/db.server.ts" <<'EOF'
import { PrismaClient } from "@prisma/client";
declare global { var prisma: PrismaClient | undefined; }
const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") global.prisma = prisma;
export default prisma;
EOF

writefile "$ROOT/app/root.tsx" <<'EOF'
export default function App() { return null; }
EOF

============================================================================

APP/LIB

============================================================================

writefile "$ROOT/app/lib/mongodb.server.ts" <<'EOF'
import { MongoClient, Db } from "mongodb";
const MONGODB_URI = process.env.MONGODB_URI!;
const MONGODB_DB = "sharedrop";
if (!MONGODB_URI) throw new Error("MONGODB_URI is required");

let cachedClient: MongoClient | null = null;
let cachedDb: Db | null = null;

export async function connectToDatabase() {
if (cachedClient && cachedDb) return { client: cachedClient, db: cachedDb };
const client = await MongoClient.connect(MONGODB_URI);
const db = client.db(MONGODB_DB);
cachedClient = client;
cachedDb = db;
return { client, db };
}

export const collections = {
SHOPS: "shops",
SHIPMENTS: "shipments",
DRIVERS: "drivers",
LEADS: "leads",
SYNC_QUEUE: "syncQueue",
OFFLINE_NOTIFICATIONS: "offlineNotifications",
USER_DEVICES: "userDevices",
VIP_CONTACTS: "vipContacts",
TENDER_SCORES: "tenderScores",
AUDIT_LOG: "auditLog",
PAYMENT_HANDOFFS: "paymentHandoffs",
DRIVING_SESSIONS: "drivingSessions",
};
EOF

writefile "$ROOT/app/lib/glie.server.ts" <<'EOF'
import { connectToDatabase, collections } from "./mongodb.server";
import { v4 as uuidv4 } from "uuid";

export interface RawLead {
id?: string; title: string; source: string; category: string;
description: string; value_gyd?: number; agency?: string;
deadline?: string; official_source?: string;
}

export interface EvaluatedLead {
lead_id: string; shop: string; title: string;
status: "QUALIFIED" | "FLAGGED_LEGAL_REVIEW";
target_entity: string; waterfall_stream: string; priority: string;
score: number; corridor_aligned: boolean; fleet_aligned: boolean;
estimated_value_gyd: number; recommended_next_step: string;
raw: RawLead; createdAt: Date; updatedAt: Date;
}

export class BusinessProfile {
operating_entity = "ShareDrop.Trading (SDT)";
holding_entity = "Guyana Hearts United Group Inc. (GY-HUG)";
dcra_fee_gyd = 63550.0;
share_distribution = { GY_HUG: 0.8, Reserved: 0.2 };
legal_counsel = "Ms. Esther Osborne";
fleet_capabilities = ["flatbed", "refrigerated", "cross_border_van", "river_freight"];
active_corridors = ["Georgetown-Lethem", "Lethem-Boa Vista", "Linden-Lethem", "Corentyne"];
waterfall_streams = [
"Stream 1: Platform Transaction Fees",
"Stream 2: Dedicated Logistics & Freight Contracts",
"Stream 3: Cross-Border Trade & Customs Facilitation",
"Stream 4: Fleet Management & Subscription Services",
"Stream 5: Commercial Tenders & Institutional Bids",
"Stream 6: Social Impact & Development Grants (GY-HUG)",
];
}

const LEGAL_FORBIDDEN_TERMS = [
"investment","securities","equity sale","shares offering","public offering",
"ipo","custody","wallet","escrow account","hold funds",
];

function containsLegalRisk(text: string): boolean {
const lower = text.toLowerCase();
return LEGAL_FORBIDDEN_TERMS.some((t) => lower.includes(t));
}

export class LeadEvaluator {
constructor(private profile: BusinessProfile = new BusinessProfile()) {}

evaluateLead(shop: string, raw: RawLead): EvaluatedLead {
const title = raw.title || "Untitled Lead";
const description = raw.description || "";
const category = (raw.category || "").toLowerCase();
const value = raw.value_gyd || 0;

}

private actionPlan(score: number, category: string, entity: string): string {
if (entity === this.profile.holding_entity)
return "Prepare GY-HUG grant proposal focusing on community welfare, local development, and regional impact.";
if (score >= 70)
return "Dispatch automated quote, schedule priority intake call, and draft SDT commercial service agreement.";
return "Add to SDT nurture pipeline; send platform capabilities deck and schedule follow-up.";
}
}

export async function persistLead(lead: EvaluatedLead) {
const { db } = await connectToDatabase();
await db.collection(collections.LEADS).insertOne(lead);
return lead;
}

export async function listLeads(shop: string, limit = 100) {
const { db } = await connectToDatabase();
return db.collection(collections.LEADS).find({ shop }).sort({ createdAt: -1 }).limit(limit).toArray();
}

export async function auditLog(shop: string, action: string, payload: any) {
const { db } = await connectToDatabase();
await db.collection(collections.AUDIT_LOG).insertOne({ shop, action, payload, at: new Date() });
}
EOF

writefile "$ROOT/app/lib/notifications.server.ts" <<'EOF'
import { connectToDatabase, collections } from "./mongodb.server";

interface PushPayload {
userId: string; title: string; body: string;
data: Record<string, any>; persistUntilDelivered?: boolean; expiresAt?: Date;
}

export async function sendPushNotification(payload: PushPayload) {
const { db } = await connectToDatabase();
const { userId, title, body, data, persistUntilDelivered, expiresAt } = payload;
const devices = await db.collection(collections.USER_DEVICES).find({ userId }).toArray();
if (devices.length === 0) return { delivered: false, reason: "No devices registered" };

const results: any[] = [];
for (const device of devices) {
const isOnline = device.lastSeen > new Date(Date.now() - 5 * 60 * 1000);
if (isOnline) {
await sendToDevice(device, { title, body, data });
results.push({ deviceId: device.deviceId, delivered: true });
} else if (persistUntilDelivered) {
await db.collection(collections.OFFLINE_NOTIFICATIONS).insertOne({
userId, deviceId: device.deviceId, notification: { title, body, data },
expiresAt, attempts: 0, maxAttempts: 10, createdAt: new Date(),
});
results.push({ deviceId: device.deviceId, delivered: false, queued: true });
} else {
results.push({ deviceId: device.deviceId, delivered: false });
}
}
return {
delivered: results.some((r) => r.delivered),
queued: results.some((r) => r.queued),
results,
};
}

async function sendToDevice(device: any, notification: any) {
console.log([push] → ${device.deviceId}:, notification.title);
return { success: true };
}
EOF

writefile "$ROOT/app/lib/fulfillment.server.ts" <<'EOF'
export async function createFulfillment(admin: any, fulfillmentOrderId: string, trackingNumber: string, trackingCompany: string) {
const res = await admin.graphql(
#graphql
    mutation fulfillmentCreateV2($fulfillment: FulfillmentV2Input!) {
      fulfillmentCreateV2(fulfillment: $fulfillment) {
        fulfillment { id status trackingInfo { number company url } }
        userErrors { field message }
      }
    },
{ variables: { fulfillment: {
lineItemsByFulfillmentOrder: [{ fulfillmentOrderId }],
trackingInfo: { number: trackingNumber, company: trackingCompany },
notifyCustomer: true,
} } }
);
const data = await res.json();
const errs = data.data?.fulfillmentCreateV2?.userErrors || [];
if (errs.length) throw new Error(errs[0].message);
return data.data?.fulfillmentCreateV2?.fulfillment;
}
EOF

writefile "$ROOT/app/lib/payments.server.ts" <<'EOF'
import { connectToDatabase } from "./mongodb.server";

/* V7.1 Non-Custody ABSOLUTE — Handbook Part IV 16
SDT Ltd is NOT a PSP. No wallet. No escrow. No custody. */

export interface P2PPaymentLink {
orderId: string; amountGyd: number; amountBrl?: number;
rider: { name: string; email: string; phone: string };
driver: { name: string; bankName?: string; pixKey?: string };
provider: "mmg" | "be_payments" | "stripe" | "pix";
nonCustody: true; createdAt: Date;
}

const PIX_CHAVE = "71086287240"; // Locked V7.1

export async function createP2PHandoff(
shop: string, orderId: string, amount: number, currency: "GYD" | "BRL" = "GYD"
): Promise<P2PPaymentLink> {
const provider = currency === "BRL" ? "pix" : amount > 200_000 ? "mmg" : "be_payments";
const link: P2PPaymentLink = {
orderId, amountGyd: currency === "GYD" ? amount : 0,
amountBrl: currency === "BRL" ? amount : undefined,
rider: { name: "", email: "", phone: "" }, driver: { name: "" },
provider, nonCustody: true, createdAt: new Date(),
};
const { db } = await connectToDatabase();
await db.collection("paymentHandoffs").insertOne({
shop, ...link,
pixChave: currency === "BRL" ? PIX_CHAVE : null,
status: "awaiting_rider_action",
});
return link;
}

export interface PlatformAccessFee {
tier: "basic" | "pro" | "business" | "enterprise";
amountGyd: number; period: "monthly";
}

export const PLATFORM_ACCESS_FEES: Record<string, PlatformAccessFee> = {
basic:      { tier: "basic",      amountGyd: 0,     period: "monthly" },
pro:        { tier: "pro",        amountGyd: 5000,  period: "monthly" },
business:   { tier: "business",   amountGyd: 15000, period: "monthly" },
enterprise: { tier: "enterprise", amountGyd: 0,     period: "monthly" },
};

const FORBIDDEN = ["wallet","escrow","custody","pool","float","hold funds","trust account"];
export function assertNonCustody(text: string): void {
const lower = text.toLowerCase();
for (const term of FORBIDDEN) {
if (lower.includes(term)) {
throw new Error(
V7.1 Non-Custody violation: term "${term}" detected.  +
Escalate to eosborne.legal@gmail.com immediately.
);
}
}
}
EOF

writefile "$ROOT/app/lib/driving-school.server.ts" <<'EOF'
import { v4 as uuidv4 } from "uuid";
import { connectToDatabase } from "./mongodb.server";

export type DrivingModule =
| "player_intake_kyc" | "country_rules_loader" | "driving_school_sim"
| "live_chat" | "music_integration" | "ride_booking"
| "cross_platform" | "ai_debugging" | "qr_connect";

export interface DrivingSession {
_id: string; shop: string; playerId: string;
country: "GY" | "BR"; rulesJson: string;
module: DrivingModule; score: number; passed: boolean; createdAt: Date;
}

export const GYHUG_MODULES: Record<DrivingModule, { label: string; weight: number }> = {
player_intake_kyc:    { label: "1. Player Intake KYC",              weight: 10 },
country_rules_loader: { label: "2. Country Rules Loader",           weight: 10 },
driving_school_sim:   { label: "3. Driving School Simulation",      weight: 25 },
live_chat:            { label: "4. Live Chat (BitChat + WebSocket)", weight: 5 },
music_integration:    { label: "5. Music Integration (Sharedrop FM)",weight: 5 },
ride_booking:         { label: "6. Ride Booking via SDT",           weight: 15 },
cross_platform:       { label: "7. Cross-Platform",                 weight: 5 },
ai_debugging:         { label: "8. AI Debugging Assistant",         weight: 15 },
qr_connect:           { label: "9. QR Connect",                     weight: 10 },
};

export const GTTI_SAMPLE_QUESTIONS = [
{ id: "GY-Q1", question: "What is the speed limit in a residential area?",
options: { A: "30 km/h", B: "50 km/h", C: "80 km/h", D: "100 km/h" }, answer: "B" },
{ id: "GY-Q2", question: "When must you wear a seatbelt?",
options: { A: "Only on highways", B: "At all times when driving", C: "Only at night", D: "Never" }, answer: "B" },
{ id: "GY-Q3", question: "Hazard detected: pedestrian crossing. You must:",
options: { A: "Speed up", B: "Honk and proceed", C: "Yield to pedestrians", D: "Stop regardless" }, answer: "C" },
];

export async function startDrivingSession(shop: string, playerId: string, country: "GY" | "BR"): Promise<DrivingSession> {
const session: DrivingSession = {
_id: uuidv4(), shop, playerId, country,
rulesJson: country === "GY" ? "guiana_rules.json" : "brazil_rules.json",
module: "player_intake_kyc", score: 0, passed: false, createdAt: new Date(),
};
const { db } = await connectToDatabase();
await db.collection("drivingSessions").insertOne(session);
return session;
}

export async function submitTheoryTest(sessionId: string, answers: Record<string, string>) {
let correct = 0;
for (const q of GTTI_SAMPLE_QUESTIONS) if (answers[q.id] === q.answer) correct++;
const score = Math.round((correct / GTTI_SAMPLE_QUESTIONS.length) * 100);
const passed = score >= 70;
const { db } = await connectToDatabase();
await db.collection("drivingSessions").updateOne(
{ _id: sessionId }, { $set: { score, passed, updatedAt: new Date() } }
);
return {
score, passed,
feedback: passed
? "Theory passed. Proceed to practical driving simulation."
: "Review GTTI handbook and retry. Minimum 70% required.",
};
}
EOF

============================================================================

ROUTES

============================================================================

writefile "$ROOT/app/routes/app.tsx" <<'EOF'
import { Outlet, useLoaderData } from "@remix-run/react";
import { boundary } from "@shopify/shopify-app-remix/server";
import { AppProvider } from "@shopify/shopify-app-remix/react";
import { NavMenu } from "@shopify/app-bridge-react";
import polarisStyles from "@shopify/polaris/build/esm/styles.css?url";
import { Links, Meta, Scripts, ScrollRestoration } from "@remix-run/react";
import { authenticate } from "../shopify.server";

export const links = () => [{ rel: "stylesheet", href: polarisStyles }];

export async function loader({ request }: any) {
await authenticate.admin(request);
return { apiKey: process.env.SHOPIFY_API_KEY || "" };
}

export default function App() {
const { apiKey } = useLoaderData<typeof loader>();
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>
<body>
<AppProvider isEmbeddedApp apiKey={apiKey}>
<NavMenu>
<a href="/app" rel="home">Dashboard</a>
<a href="/app/leads">GLIE Leads</a>
<a href="/app/shipments">Shipments</a>
<a href="/app/shipments/new">Create Shipment</a>
<a href="/app/vip-drivers">VIP Drivers</a>
<a href="/app/drivers">Driver Fleet</a>
<a href="/app/gameplay">Gameplay Flow</a>
<a href="/app/driving-school">GY-HUG Driving School</a>
<a href="/app/payments">Payments (Non-Custody)</a>
</NavMenu>
<Outlet />
</AppProvider>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}

export const ErrorBoundary = boundary.error((error: any) => (

  <div style={{ padding: 20 }}>
    <h1>Error</h1>
    <pre>{error?.statusText || error?.message}</pre>
  </div>
));
EOF

writefile "$ROOT/app/routes/_index.tsx" <<'EOF'
import { redirect } from "@remix-run/node";
export const loader = () => redirect("/app");
EOF

writefile "$ROOT/app/routes/auth.$.tsx" <<'EOF'
import { authenticate } from "../shopify.server";
export const loader = async ({ request }: any) => {
await authenticate.admin(request);
return null;
};
EOF

writefile "$ROOT/app/routes/app._index.tsx" <<'EOF'
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, Button, IndexTable, Badge, InlineGrid } from "@shopify/polaris";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";

export async function loader({ request }: LoaderFunctionArgs) {
const { session } = await authenticate.admin(request);
const shop = session.shop;
const { db } = await connectToDatabase();

const [shipments, leads, stats] = await Promise.all([
db.collection(collections.SHIPMENTS).find({ shop }).sort({ createdAt: -1 }).limit(10).toArray(),
db.collection(collections.LEADS).find({ shop }).sort({ createdAt: -1 }).limit(10).toArray(),
(async () => ({
shipmentsTotal: await db.collection(collections.SHIPMENTS).countDocuments({ shop }),
pending: await db.collection(collections.SHIPMENTS).countDocuments({ shop, status: "pending" }),
leadsQualified: await db.collection(collections.LEADS).countDocuments({ shop, status: "QUALIFIED" }),
leadsFlagged: await db.collection(collections.LEADS).countDocuments({ shop, status: "FLAGGED_LEGAL_REVIEW" }),
}))(),
]);

return json({ shipments, leads, stats });
}

function StatCard({ label, value }: { label: string; value: number }) {
return (
<Card>
<BlockStack gap="100">
<Text as="p" variant="bodySm" tone="subdued">{label}</Text>
<Text as="p" variant="headingLg">{value}</Text>
</BlockStack>
</Card>
);
}

export default function Dashboard() {
const { shipments, leads, stats } = useLoaderData<typeof loader>();
return (
<Page title="Sharedrop.Trading — Operations Dashboard">
<Layout>
<Layout.Section>
<BlockStack gap="400">
<InlineGrid columns={4} gap="300">
<StatCard label="Shipments" value={stats.shipmentsTotal} />
<StatCard label="Pending" value={stats.pending} />
<StatCard label="Leads Qualified" value={stats.leadsQualified} />
<StatCard label="Legal Flags" value={stats.leadsFlagged} />
</InlineGrid>

);
}
EOF

writefile "$ROOT/app/routes/app.leads.tsx" <<'EOF'
import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, useFetcher } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, IndexTable, Badge, Button, Banner, TextField, Select } from "@shopify/polaris";
import { useState } from "react";
import { authenticate } from "../shopify.server";
import { LeadEvaluator, persistLead, listLeads, auditLog } from "../lib/glie.server";

export async function loader({ request }: LoaderFunctionArgs) {
const { session } = await authenticate.admin(request);
const leads = await listLeads(session.shop);
return json({ leads });
}

export async function action({ request }: ActionFunctionArgs) {
const { session } = await authenticate.admin(request);
const form = await request.formData();
const raw = {
title: String(form.get("title") || ""),
source: String(form.get("source") || "Manual"),
category: String(form.get("category") || "freight"),
description: String(form.get("description") || ""),
value_gyd: Number(form.get("value_gyd") || 0),
};
const evaluator = new LeadEvaluator();
const evaluated = evaluator.evaluateLead(session.shop, raw);
await persistLead(evaluated);
await auditLog(session.shop, "lead.created", { lead_id: evaluated.lead_id });
return json({ lead: evaluated });
}

export default function LeadsPage() {
const { leads } = useLoaderData<typeof loader>();
const fetcher = useFetcher<typeof action>();
const [title, setTitle] = useState("");
const [source, setSource] = useState("Manual");
const [category, setCategory] = useState("freight procurement");
const [description, setDescription] = useState("");
const [value, setValue] = useState("0");

const submit = () => fetcher.submit({ title, source, category, description, value_gyd: value }, { method: "post" });

return (
<Page title="GLIE V7.0 — Lead Engine" subtitle="Dual-entity routing · 6-stream waterfall · legal firewall">
<Layout>
<Layout.Section>
<Banner tone="info">Non-PSP ABSOLUTE. Investment / securities / custody terms auto-escalate to Ms. Esther Osborne.</Banner>
</Layout.Section>
<Layout.Section>
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Evaluate New Lead</Text>
<TextField label="Title" value={title} onChange={setTitle} autoComplete="off" />
<Select label="Source" value={source} onChange={setSource} options={[
{ label: "Manual", value: "Manual" },
{ label: "Government Tender Board", value: "Government Tender Board" },
{ label: "NGO", value: "NGO" },
{ label: "Webhook", value: "Webhook" },
]} />
<Select label="Category" value={category} onChange={setCategory} options={[
{ label: "Freight Procurement", value: "freight procurement" },
{ label: "Cross-Border", value: "cross-border customs" },
{ label: "Commercial Tender", value: "tender procurement" },
{ label: "Grant / Social Impact", value: "grant social impact" },
{ label: "Investment Inquiry", value: "investment" },
]} />
<TextField label="Description" value={description} onChange={setDescription} multiline={4} autoComplete="off" />
<TextField label="Estimated Value (GYD)" type="number" value={value} onChange={setValue} autoComplete="off" />
<Button variant="primary" onClick={submit} loading={fetcher.state === "submitting"}>Evaluate Lead</Button>

);
}
EOF

writefile "$ROOT/app/routes/app.shipments.tsx" <<'EOF'
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData, Link } from "@remix-run/react";
import { Page, Layout, Card, IndexTable, Badge, Button, Text, BlockStack } from "@shopify/polaris";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";

export async function loader({ request }: LoaderFunctionArgs) {
const { session } = await authenticate.admin(request);
const { db } = await connectToDatabase();
const shipments = await db.collection(collections.SHIPMENTS).find({ shop: session.shop }).sort({ createdAt: -1 }).limit(200).toArray();
return json({ shipments });
}

export default function Shipments() {
const { shipments } = useLoaderData<typeof loader>();
return (
<Page title="Shipments"
primaryAction={<Link to="/app/shipments/new"><Button variant="primary">Create Shipment</Button></Link>}>
<Layout>
<Layout.Section>
<Card>
<BlockStack gap="200">
<Text as="h2" variant="headingMd">All Shipments</Text>
<IndexTable resourceName={{ singular: "shipment", plural: "shipments" }} itemCount={shipments.length}
headings={[{ title: "Client ID" }, { title: "Pickup" }, { title: "Delivery" }, { title: "Gender Pref" }, { title: "Status" }]}
selectable={false}>
{shipments.map((s: any, i) => (
<IndexTable.Row id={s._id} key={s._id} position={i}>
<IndexTable.Cell>{s.clientId}</IndexTable.Cell>
<IndexTable.Cell>{s.pickup?.location || "—"}</IndexTable.Cell>
<IndexTable.Cell>{s.delivery?.location || "—"}</IndexTable.Cell>
<IndexTable.Cell>{s.preferences?.driverGender || "any"}</IndexTable.Cell>
<IndexTable.Cell>
<Badge tone={s.status === "delivered" ? "success" : s.status === "in_transit" ? "info" : "attention"}>{s.status}</Badge>
</IndexTable.Cell>
</IndexTable.Row>
))}
</IndexTable>
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.shipments.new.tsx" <<'EOF'
import { json, type ActionFunctionArgs, type LoaderFunctionArgs } from "@remix-run/node";
import { useActionData, useNavigate, useSubmit } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, TextField, Select, Button, RadioButton, Banner } from "@shopify/polaris";
import { useState } from "react";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";
import { v4 as uuidv4 } from "uuid";

export async function loader({ request }: LoaderFunctionArgs) {
await authenticate.admin(request);
return json({ ok: true });
}

export async function action({ request }: ActionFunctionArgs) {
const { session } = await authenticate.admin(request);
const form = await request.formData();
const { db } = await connectToDatabase();
const shipment = {
_id: uuidv4(), shop: session.shop,
clientId: shipment_${Date.now()}_${uuidv4()},
description: form.get("description"),
cargoType: form.get("cargoType"),
weight: Number(form.get("weight") || 0),
pickup: { location: form.get("pickupLocation"), date: form.get("pickupDate") },
delivery: { location: form.get("deliveryLocation"), date: form.get("deliveryDate") },
preferences: { driverGender: form.get("driverGender") || "any" },
status: "pending", syncStatus: "synced",
createdAt: new Date(), updatedAt: new Date(),
};
await db.collection(collections.SHIPMENTS).insertOne(shipment);
return json({ success: true, shipment });
}

export default function NewShipment() {
const actionData = useActionData<typeof action>();
const submit = useSubmit();
const navigate = useNavigate();
const [gender, setGender] = useState("any");

if (actionData?.success) navigate("/app/shipments");

return (
<Page title="Create Shipment" backAction={{ content: "Back", onAction: () => navigate("/app") }}>
<Layout>
<Layout.Section>
<form method="post" onSubmit={(e) => { e.preventDefault(); submit(e.currentTarget); }}>
<BlockStack gap="400">
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Shipment Details</Text>
<TextField label="Description" name="description" autoComplete="off" required />
<Select label="Cargo Type" name="cargoType" options={[
{ label: "General Goods", value: "general" },
{ label: "Construction Materials", value: "construction" },
{ label: "Agricultural Products", value: "agriculture" },
{ label: "Medical Supplies", value: "medical" },
]} />
<TextField label="Weight (kg)" name="weight" type="number" autoComplete="off" />
</BlockStack>
</Card>
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Pickup & Delivery</Text>
<TextField label="Pickup Location" name="pickupLocation" autoComplete="off" required />
<TextField label="Pickup Date" name="pickupDate" type="date" autoComplete="off" />
<TextField label="Delivery Location" name="deliveryLocation" autoComplete="off" required />
<TextField label="Delivery Date" name="deliveryDate" type="date" autoComplete="off" />
</BlockStack>
</Card>
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Safety Preferences</Text>
<Banner tone="info">Select preferred driver gender. Verified against KYC during onboarding.</Banner>
<RadioButton label="No preference" name="g" checked={gender === "any"} onChange={() => setGender("any")} />
<RadioButton label="Female drivers only" name="g" checked={gender === "female"} onChange={() => setGender("female")} />
<RadioButton label="Male drivers only" name="g" checked={gender === "male"} onChange={() => setGender("male")} />
<input type="hidden" name="driverGender" value={gender} />
</BlockStack>
</Card>
<Button variant="primary" submit>Create Shipment</Button>
</BlockStack>
</form>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.vip-drivers.tsx" <<'EOF'
import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, useFetcher } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, Button, ResourceList, ResourceItem, Badge, Banner, Select, TextField } from "@shopify/polaris";
import { useState } from "react";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";
import { sendPushNotification } from "../lib/notifications.server";
import { v4 as uuidv4 } from "uuid";

export async function loader({ request }: LoaderFunctionArgs) {
const { session } = await authenticate.admin(request);
const { db } = await connectToDatabase();
const drivers = await db.collection(collections.DRIVERS).find({ shop: session.shop, status: "active" }).sort({ "ratings.average": -1 }).limit(25).toArray();
return json({ drivers });
}

export async function action({ request }: ActionFunctionArgs) {
const { session } = await authenticate.admin(request);
const { driverId, shipmentId, message } = await request.json();
const { db } = await connectToDatabase();
const driver = await db.collection(collections.DRIVERS).findOne({ _id: driverId, shop: session.shop });
if (!driver) return json({ error: "Driver not found" }, { status: 404 });
const contact = {
_id: uuidv4(), shop: session.shop, driverId, shipmentId, message,
status: "pending", createdAt: new Date(),
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
};
await db.collection(collections.VIP_CONTACTS).insertOne(contact);
const notification = await sendPushNotification({
userId: driver.userId || driver._id,
title: "🚀 VIP Shipment Request",
body: VIP client requested you for shipment ${shipmentId},
data: { type: "vip_request", contactId: contact._id, shipmentId },
persistUntilDelivered: true, expiresAt: contact.expiresAt,
});
return json({ contact, notification });
}

export default function VIPDrivers() {
const { drivers } = useLoaderData<typeof loader>();
const fetcher = useFetcher();
const [selected, setSelected] = useState<any>(null);
const [message, setMessage] = useState("");
const [vehicle, setVehicle] = useState("all");
const [gender, setGender] = useState("any");

const filtered = drivers.filter((d: any) => {
if (vehicle !== "all" && d.vehicleType !== vehicle) return false;
if (gender !== "any" && d.gender !== gender) return false;
return true;
});

const send = () => {
if (!selected) return;
fetcher.submit(JSON.stringify({ driverId: selected._id, shipmentId: "demo", message }),
{ method: "post", encType: "application/json" });
};

return (
<Page title="VIP Driver Contact" subtitle="Reach drivers even when they're offline">
<Layout>
<Layout.Section>
<Banner tone="info">Requests are queued and delivered when the driver reconnects. Non-PSP: no funds held.</Banner>
</Layout.Section>
<Layout.Section>
<Card>
<BlockStack gap="300">
<div style={{ display: "flex", gap: 12 }}>
<Select label="Vehicle" value={vehicle} onChange={setVehicle} options={[
{ label: "All", value: "all" }, { label: "Car", value: "car" },
{ label: "Motorcycle", value: "motorcycle" }, { label: "Truck", value: "truck" },
]} />
<Select label="Gender" value={gender} onChange={setGender} options={[
{ label: "Any", value: "any" }, { label: "Female", value: "female" }, { label: "Male", value: "male" },
]} />
</div>
<ResourceList
resourceName={{ singular: "driver", plural: "drivers" }}
items={filtered.map((d: any) => ({
id: d._id, name: d.name, rating: d.ratings?.average || 0,
trips: d.completedTrips || 0, vehicleType: d.vehicleType, status: d.status,
}))}
renderItem={(item) => (
<ResourceItem id={item.id} onClick={() => setSelected(item)} accessibilityLabel={Select ${item.name}}>
<BlockStack gap="100">
<div style={{ display: "flex", gap: 8, alignItems: "center" }}>
<Text as="span" fontWeight="semibold">{item.name}</Text>
<Badge tone={item.status === "active" ? "success" : "attention"}>{item.status}</Badge>
</div>
<Text as="p" variant="bodySm" tone="subdued">
⭐ {item.rating.toFixed(1)} · {item.trips} trips · {item.vehicleType}
</Text>
</BlockStack>
</ResourceItem>
)}
/>
{selected && (
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Message to {selected.name}</Text>
<TextField label="Personal message" value={message} onChange={setMessage} multiline={3} autoComplete="off" />
<Button variant="primary" onClick={send} loading={fetcher.state === "submitting"}>Send VIP Request</Button>
</BlockStack>
)}
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.drivers.tsx" <<'EOF'
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, IndexTable, Badge } from "@shopify/polaris";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";

export async function loader({ request }: LoaderFunctionArgs) {
const { session } = await authenticate.admin(request);
const { db } = await connectToDatabase();
const drivers = await db.collection(collections.DRIVERS).find({ shop: session.shop }).sort({ createdAt: -1 }).toArray();
return json({ drivers });
}

export default function Drivers() {
const { drivers } = useLoaderData<typeof loader>();
return (
<Page title="Driver Fleet">
<Layout>
<Layout.Section>
<Card>
<BlockStack gap="200">
<Text as="h2" variant="headingMd">All Drivers</Text>
<IndexTable resourceName={{ singular: "driver", plural: "drivers" }} itemCount={drivers.length}
headings={[{ title: "Name" }, { title: "Vehicle" }, { title: "Rating" }, { title: "Trips" }, { title: "Status" }]}
selectable={false}>
{drivers.map((d: any, i) => (
<IndexTable.Row id={d._id} key={d._id} position={i}>
<IndexTable.Cell>{d.name}</IndexTable.Cell>
<IndexTable.Cell>{d.vehicleType || "—"}</IndexTable.Cell>
<IndexTable.Cell>⭐ {(d.ratings?.average || 0).toFixed(1)}</IndexTable.Cell>
<IndexTable.Cell>{d.completedTrips || 0}</IndexTable.Cell>
<IndexTable.Cell>
<Badge tone={d.status === "active" ? "success" : "attention"}>{d.status}</Badge>
</IndexTable.Cell>
</IndexTable.Row>
))}
</IndexTable>
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.gameplay.tsx" <<'EOF'
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, InlineGrid, Badge } from "@shopify/polaris";
import { authenticate } from "../shopify.server";

const FLOW = [
{ n: 1, title: "Player Signs Up",               desc: "Profile stored via Sharedrop.Trading API" },
{ n: 2, title: "Country Selected",              desc: "Rules JSON loaded: Guyana / Brazil" },
{ n: 3, title: "Simulation Engine Runs",        desc: "Written + Practical Tests · GTTI Standards" },
{ n: 4, title: "Live Chat + Sharedrop FM",      desc: "Music + real-time comms" },
{ n: 5, title: "Ride Booking + Cargo Logistics",desc: "85% Driver Share" },
{ n: 6, title: "QR Verification",               desc: "WhatsApp / Sekai roleplay" },
{ n: 7, title: "AI Debugging",                  desc: "Keeps gameplay error-free" },
{ n: 8, title: "Progress → Real Onboarding",    desc: "Feeds into driver pipeline · Fleet expansion" },
];

export async function loader({ request }: LoaderFunctionArgs) {
await authenticate.admin(request);
return json({ flow: FLOW });
}

export default function Gameplay() {
const { flow } = useLoaderData<typeof loader>();
return (
<Page title="Gameplay Flow — 8 Steps to Real Earnings" subtitle="Branded · Luxury · Scalable">
<Layout>
<Layout.Section>
<InlineGrid columns={4} gap="300">
{flow.map((s) => (
<Card key={s.n}>
<BlockStack gap="100">
<Badge tone="info">STEP {s.n}</Badge>
<Text as="p" fontWeight="semibold">{s.title}</Text>
<Text as="p" variant="bodySm" tone="subdued">{s.desc}</Text>
</BlockStack>
</Card>
))}
</InlineGrid>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.driving-school.tsx" <<'EOF'
import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, useFetcher } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, Button, InlineGrid, Badge, Banner, RadioButton } from "@shopify/polaris";
import { useState } from "react";
import { authenticate } from "../shopify.server";
import { GYHUG_MODULES, GTTI_SAMPLE_QUESTIONS, startDrivingSession, submitTheoryTest } from "../lib/driving-school.server";

export async function loader({ request }: LoaderFunctionArgs) {
await authenticate.admin(request);
return json({ modules: GYHUG_MODULES, questions: GTTI_SAMPLE_QUESTIONS });
}

export async function action({ request }: ActionFunctionArgs) {
const { session } = await authenticate.admin(request);
const form = await request.formData();
const intent = String(form.get("intent"));
if (intent === "start") {
const country = (String(form.get("country")) || "GY") as "GY" | "BR";
const s = await startDrivingSession(session.shop, "player-demo", country);
return json({ session: s });
}
if (intent === "submit") {
const sessionId = String(form.get("sessionId"));
const answers: Record<string, string> = {};
for (const [k, v] of form.entries()) if (k.startsWith("q_")) answers[k.replace("q_", "")] = String(v);
const result = await submitTheoryTest(sessionId, answers);
return json({ result });
}
return json({ error: "Unknown intent" }, { status: 400 });
}

export default function DrivingSchool() {
const { modules, questions } = useLoaderData<typeof loader>();
const fetcher = useFetcher<any>();
const [answers, setAnswers] = useState<Record<string, string>>({});

return (
<Page title="GY-HUG Driving School Simulation" subtitle="Certified by GTTI Standards · Superapp Module V1.0">
<Layout>
<Layout.Section>
<Banner tone="info">9 core modules. Offline-first. Works in Region 9 without cellular.</Banner>
</Layout.Section>
<Layout.Section>
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Module Map</Text>
<InlineGrid columns={3} gap="300">
{Object.entries(modules).map(([key, m]: any) => (
<Card key={key}>
<BlockStack gap="100">
<Text as="p" fontWeight="semibold">{m.label}</Text>
<Badge tone="success">weight {m.weight}%</Badge>
</BlockStack>
</Card>
))}
</InlineGrid>
</BlockStack>
</Card>
</Layout.Section>
<Layout.Section>
<Card>
<BlockStack gap="300">
<Text as="h2" variant="headingMd">Theory Test — GTTI Standards</Text>
{questions.map((q, idx) => (
<BlockStack key={q.id} gap="100">
<Text as="p" fontWeight="semibold">Q{idx + 1}. {q.question}</Text>
{Object.entries(q.options).map(([k, v]) => (
<RadioButton key={k} label={${k}) ${v}} name={q_${q.id}}
checked={answers[q.id] === k} onChange={() => setAnswers({ ...answers, [q.id]: k })} />
))}
</BlockStack>
))}
<Button variant="primary" onClick={() =>
fetcher.submit(
{ intent: "submit", sessionId: "demo",
...Object.fromEntries(Object.entries(answers).map(([k, v]) => [q_${k}, v])) },
{ method: "post" }
)}>Submit Test</Button>
{fetcher.data?.result && (
<Banner tone={fetcher.data.result.passed ? "success" : "critical"}
title={Score: ${fetcher.data.result.score}% · ${fetcher.data.result.passed ? "PASSED" : "FAILED"}}>
{fetcher.data.result.feedback}
</Banner>
)}
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/app.payments.tsx" <<'EOF'
import { json, type LoaderFunctionArgs, type ActionFunctionArgs } from "@remix-run/node";
import { useLoaderData, useFetcher } from "@remix-run/react";
import { Page, Layout, Card, Text, BlockStack, Button, Banner, Badge, InlineGrid } from "@shopify/polaris";
import { authenticate } from "../shopify.server";
import { PLATFORM_ACCESS_FEES, createP2PHandoff, assertNonCustody } from "../lib/payments.server";

const PIX_CHAVE = "71086287240";

export async function loader({ request }: LoaderFunctionArgs) {
await authenticate.admin(request);
return json({ fees: PLATFORM_ACCESS_FEES, pixChave: PIX_CHAVE });
}

export async function action({ request }: ActionFunctionArgs) {
const { session } = await authenticate.admin(request);
const form = await request.formData();
const intent = String(form.get("intent"));
try {
if (intent === "handoff") {
const amount = Number(form.get("amount") || 0);
const currency = (String(form.get("currency")) || "GYD") as "GYD" | "BRL";
assertNonCustody(create handoff amount=${amount} currency=${currency});
const link = await createP2PHandoff(session.shop, String(form.get("orderId")), amount, currency);
return json({ link });
}
} catch (e: any) {
return json({ error: e.message }, { status: 400 });
}
return json({ error: "Unknown intent" }, { status: 400 });
}

export default function Payments() {
const { fees, pixChave } = useLoaderData<typeof loader>();
const fetcher = useFetcher<any>();

return (
<Page title="Payments — Day 1 Non-Custody Model"
subtitle="V7.1 · P2P Direct · No Wallet · No Escrow · PCI-Compliant · Non-PSP">
<Layout>
<Layout.Section>
<Banner tone="warning" title="V7.1 Legal Boundary — Absolute">
SDT Ltd does not hold, store, or control any rider or driver funds. All payments flow
Rider → Licensed Partner (Be!, MMG, Stripe, PIX) → Driver directly. Platform Access Fees
are invoiced separately by SDT Ltd (software license only).
</Banner>
</Layout.Section>
<Layout.Section>
<InlineGrid columns={2} gap="300">
<Card>
<BlockStack gap="200">
<Text as="h2" variant="headingMd">P2P Payment Handoff</Text>
<Text as="p" variant="bodySm">Generate a deep link to a licensed PSP. SDT never touches funds.</Text>
<Button variant="primary" onClick={() =>
fetcher.submit({ intent: "handoff", orderId: "demo", amount: "45000", currency: "GYD" }, { method: "post" })}>
Create GYD Handoff
</Button>
<Button onClick={() =>
fetcher.submit({ intent: "handoff", orderId: "demo", amount: "320", currency: "BRL" }, { method: "post" })}>
Create BRL Handoff (PIX)
</Button>
{fetcher.data?.link && (
<Banner tone="success" title="Handoff created">
Provider: {fetcher.data.link.provider} · Non-custody: true
</Banner>
)}
{fetcher.data?.error && (
<Banner tone="critical" title="Blocked by V7.1 firewall">{fetcher.data.error}</Banner>
)}
</BlockStack>
</Card>
<Card>
<BlockStack gap="200">
<Text as="h2" variant="headingMd">Platform Access Fees</Text>
<Text as="p" variant="bodySm">Software license · Invoiced by SDT Ltd</Text>
{Object.values(fees).map((f: any) => (
<div key={f.tier} style={{ display: "flex", justifyContent: "space-between" }}>
<Text as="span" fontWeight="semibold">{f.tier.toUpperCase()}</Text>
<Text as="span">{f.amountGyd === 0 ? "FREE / Custom" : GYD ${f.amountGyd.toLocaleString()}/mo}</Text>
</div>
))}
</BlockStack>
</Card>
</InlineGrid>
</Layout.Section>
<Layout.Section>
<Card>
<BlockStack gap="200">
<Text as="h2" variant="headingMd">Brazil Instant Payments</Text>
<Badge tone="success">PIX · Instant · Non-Custodial</Badge>
<Text as="p">PIX Chave: <strong>{pixChave}</strong></Text>
<Text as="p" variant="bodySm" tone="subdued">
Aceitamos PIX · Payment flows directly to driver's nominated account.
SDT Brazil Ltda corporate account for Platform Access Fees only.
</Text>
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
EOF

writefile "$ROOT/app/routes/webhooks.tsx" <<'EOF'
import { type ActionFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import { connectToDatabase, collections } from "../lib/mongodb.server";
import { v4 as uuidv4 } from "uuid";

export async function action({ request }: ActionFunctionArgs) {
const { topic, shop, admin, payload } = await authenticate.webhook(request);
if (!admin) return new Response();
const { db } = await connectToDatabase();

switch (topic) {
case "ORDERS_PAID": {
const order: any = payload;
await db.collection(collections.SHIPMENTS).insertOne({
_id: uuidv4(), shop, orderId: String(order.id), orderName: order.name,
customer: { email: order.email, name: ${order.customer?.first_name || ""} ${order.customer?.last_name || ""}.trim() },
pickup: { location: "Lethem Hub", date: new Date() },
delivery: { location: order.shipping_address ? ${order.shipping_address.address1}, ${order.shipping_address.city} : "Customer Address", date: new Date() },
status: "pending", syncStatus: "synced",
clientId: shopify_${order.id},
preferences: { driverGender: "any" },
createdAt: new Date(), updatedAt: new Date(),
});
break;
}
case "FULFILLMENTS_CREATE":
case "FULFILLMENTS_UPDATE": {
const f: any = payload;
await db.collection(collections.SHIPMENTS).updateOne(
{ shop, orderId: String(f.order_id) },
{ $set: { status: f.status === "success" ? "in_transit" : f.status,
          trackingNumber: f.tracking_number, trackingCompany: f.tracking_company, updatedAt: new Date() } });
      break;
    }
    case "ORDERS_FULFILLED": {
      const order: any = payload;
      await db.collection(collections.SHIPMENTS).updateOne(
        { shop, orderId: String(order.id) },
        { $set: { status: "delivered", updatedAt: new Date() } });
break;
}
case "CUSTOMERS_CREATE": {
const c: any = payload;
await db.collection("customers").updateOne(
{ shop, customerId: String(c.id) },
{ $set: { shop, customerId: String(c.id),
emailHash: c.email ? Buffer.from(c.email).toString("base64") : null,
region: c.default_address?.province || null, updatedAt: new Date() } },
{ upsert: true });
break;
}
}
return new Response();
}
EOF

============================================================================

SERVICE WORKER + PUBLIC

============================================================================

writefile "$ROOT/public/service-worker.js" <<'EOF'
const CACHE_NAME = "sharedrop-admin-v7";
const API_PREFIX = "/api";
const PRECACHE = ["/", "/app", "/app/leads", "/app/shipments", "/app/vip-drivers", "/app/driving-school", "/app/payments"];

self.addEventListener("install", (event) => {
event.waitUntil(caches.open(CACHE_NAME).then((c) => c.addAll(PRECACHE)).then(() => self.skipWaiting()));
});
self.addEventListener("activate", (event) => {
event.waitUntil(caches.keys().then((names) =>
Promise.all(names.filter((n) => n !== CACHE_NAME).map((n) => caches.delete(n)))
).then(() => self.clients.claim()));
});
self.addEventListener("fetch", (event) => {
const { request } = event;
const url = new URL(request.url);
if (request.method !== "GET") { event.respondWith(fetch(request)); return; }
if (url.pathname.startsWith(API_PREFIX)) { event.respondWith(networkFirst(request)); return; }
event.respondWith(cacheFirst(request));
});
async function networkFirst(request) {
const cache = await caches.open(CACHE_NAME);
try {
const res = await fetch(request);
if (res.ok) cache.put(request, res.clone());
return res;
} catch (err) {
const cached = await cache.match(request);
if (cached) return cached;
throw err;
}
}
async function cacheFirst(request) {
const cached = await caches.match(request);
if (cached) return cached;
try {
const res = await fetch(request);
if (res.ok) { const cache = await caches.open(CACHE_NAME); cache.put(request, res.clone()); }
return res;
} catch (err) {
if (request.mode === "navigate") return caches.match("/app");
throw err;
}
}
self.addEventListener("sync", (event) => {
if (event.tag === "sync-requests") {
event.waitUntil((async () => {
const clients = await self.clients.matchAll();
clients.forEach((c) => c.postMessage({ type: "PERFORM_SYNC" }));
})());
}
});
EOF

writefile "$ROOT/public/offline.html" <<'EOF'

<!DOCTYPE html>

<html lang="en">
<head>
<meta charset="utf-8" />
<title>Sharedrop — Offline</title>
<style>
  body { font-family: -apple-system, sans-serif; background: #0b1b3d; color: #fff;
         display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; text-align: center; }
  .card { max-width: 480px; padding: 40px; }
  h1 { color: #d4af37; margin-bottom: 8px; }
  p { opacity: 0.85; line-height: 1.6; }
</style>
</head>
<body>
  <div class="card">
    <h1>Offline</h1>
    <p>Sharedrop.Trading is offline-first. Your changes will sync automatically when the connection is restored.</p>
    <p><a href="/app" style="color:#d4af37">Retry</a></p>
  </div>
</body>
</html>
EOF

============================================================================

CHECKOUT EXTENSION

============================================================================

writefile "$ROOT/extensions/delivery-date/shopify.extension.toml" <<'EOF'
api_version = "2026-04"

[[extensions]]
name = "Sharedrop Delivery Date"
handle = "delivery-date"
type = "ui_extension"

[[extensions.targeting]]
module = "./src/Checkout.tsx"
target = "purchase.checkout.delivery-address.render-after"

[extensions.capabilities]
api_access = true
network_access = true
EOF

writefile "$ROOT/extensions/delivery-date/src/Checkout.tsx" <<'EOF'
import {
reactExtension, useApplyMetafieldsChange, useShippingAddress, useCartLines,
BlockStack, Text, DateField, Banner,
} from "@shopify/ui-extensions-react/checkout";
import { useState } from "react";

export default reactExtension("purchase.checkout.delivery-address.render-after", () => <Extension />);

function Extension() {
const [date, setDate] = useState<string>("");
const applyMetafield = useApplyMetafieldsChange();
const addr = useShippingAddress();
const lines = useCartLines();

const onChange = (value: string) => {
setDate(value);
applyMetafield({
namespace: "sharedrop", key: "preferred_delivery_date",
value, type: "single_line_text_field",
});
};

return (
<BlockStack spacing="tight">
<Banner status="info">
<Text>Sharedrop coordinates delivery. Choose your preferred date.</Text>
</Banner>
<DateField label="Preferred delivery date" value={date} onChange={onChange} />
{addr?.city && (
<Text size="small" appearance="subdued">Delivering to {addr.city}, {addr.countryCode}</Text>
)}
<Text size="small" appearance="subdued">{lines.length} item(s) in cart</Text>
</BlockStack>
);
}
EOF

============================================================================

DOCS

============================================================================

writefile "$ROOT/docs/AGENT_INSTRUCTIONS.md" <<'EOF'

Agent Instructions — Locked Decisions V7.1

1. Maps API

Leaflet + OpenStreetMap (free). Fallback: Mapbox for vector tiles.

2. Payments (V7.1 Non-Custody ABSOLUTE)

· Mock initially. Swap to licensed PSP when keys provided.
· NO wallet. NO escrow. NO custody. NO pooled funds.
· P2P: Rider → Licensed Partner → Driver bank account.
· Platform Access Fee: invoiced separately by SDT Ltd.
· PIX Chave: 71086287240
· Every payment record MUST call assertNonCustody().

3. Real-time

Socket.io. Namespaces: /rides, /chat, /alerts.
Fallback: polling 15s for offline regions.

4. Build Order

1. Phase 1: Job Board + Profiles + Ratings + KYC
2. Phase 2: Live Map + Matching + Offline sync
3. GY-HUG Driving School (9 modules)
4. Gameplay Flow (8 steps)
5. Payments V7.1 UI
6. Trading + Ratings
7. Admin Dashboard

5. DO NOT BUILD

· Wallet, escrow, custody, pooled funds
· Securities/investment intake
· Any feature holding rider/driver money
  EOF

writefile "$ROOT/docs/LEGAL_FIREWALL.md" <<'EOF'

V7.1 Legal Firewall — ABSOLUTE

Non-Custody Model

· SDT Ltd is NOT a PSP (Handbook Part IV 16)
· No wallet, escrow, custody, or pooled funds
· All payments via licensed external PSPs

Forbidden Terms (auto-escalate)

investment, securities, equity sale, shares offering, public offering, ipo,
custody, wallet, escrow account, hold funds

Escalation Target

eosborne.legal@gmail.com (Ms. Esther Osborne)

Enforcement

Every payment record calls assertNonCustody() from app/lib/payments.server.ts
EOF

writefile "$ROOT/docs/DEPLOY.md" <<'EOF'

Deployment Runbook

```bash
# 1. Install
npm install
npx prisma generate
npx prisma db push

# 2. Env
cp .env.example .env
# Fill SHOPIFY_API_KEY, SHOPIFY_API_SECRET, MONGODB_URI

# 3. Local dev
shopify app dev

# 4. Deploy to Fly.io
fly launch --no-deploy
fly secrets set SHOPIFY_API_KEY=... SHOPIFY_API_SECRET=... \
  SHOPIFY_APP_URL=https://s...content-available-to-author-only...y.dev \
  MONGODB_URI="mongodb+srv://..." \
  LEGAL_COUNSEL_EMAIL=eosborne.legal@gmail.com \
  NON_PSP_MODE=true \
  PIX_CHAVE=71086287240
fly deploy

# 5. Push config to Shopify
shopify app deploy
```

EOF

============================================================================

ZIPPING

============================================================================

echo ""
echo "📦 Packaging $ROOT..."
zip -r "$ZIP" "$ROOT" -x "/node_modules/" ".sqlite" > /dev/null

echo ""
echo "✅ Created: $ZIP"
echo ""
echo "📂 Files included:"
find "$ROOT" -type f | sort | sed "s|^|   |"
echo ""
echo "📊 Size: $(du -h "$ZIP" | cut -f1)"
echo ""
echo "🚀 To use:"
echo "   unzip $ZIP"
echo "   cd $ROOT"
echo "   npm install"
echo "   npx prisma generate && npx prisma db push"
echo "   cp .env.example .env   # fill values"
echo "   shopify app dev"

```

---

## HOW TO RUN

```bash
# 1. Save the script as build_sharedrop_v7_1.sh
chmod +x build_sharedrop_v7_1.sh

# 2. Run it
./build_sharedrop_v7_1.sh

# 3. Zip appears in current directory
ls -la sharedrop-trading-v7.1.zip
```

---

WHAT'S IN THE ZIP

```
sharedrop-trading-v7.1/
├── README.md
├── .env.example
├── .gitignore
├── package.json
├── tsconfig.json
├── shopify.app.toml
├── Dockerfile
├── fly.toml
├── prisma/
│   └── schema.prisma
├── app/
│   ├── shopify.server.ts
│   ├── db.server.ts
│   ├── root.tsx
│   ├── lib/
│   │   ├── mongodb.server.ts
│   │   ├── glie.server.ts              ← GLIE V7.0 engine
│   │   ├── notifications.server.ts
│   │   ├── fulfillment.server.ts
│   │   ├── payments.server.ts          ← V7.1 Non-Custody
│   │   └── driving-school.server.ts    ← GY-HUG module
│   └── routes/
│       ├── app.tsx                     ← NavMenu (9 items)
│       ├── _index.tsx
│       ├── auth.$.tsx
│       ├── app._index.tsx              ← Dashboard
│       ├── app.leads.tsx               ← GLIE lead engine
│       ├── app.shipments.tsx
│       ├── app.shipments.new.tsx       ← Safety preferences
│       ├── app.vip-drivers.tsx         ← Offline contact
│       ├── app.drivers.tsx
│       ├── app.gameplay.tsx            ← 8-step flow
│       ├── app.driving-school.tsx      ← 9 GTTI modules
│       ├── app.payments.tsx            ← Non-custody UI
│       └── webhooks.tsx
├── public/
│   ├── service-worker.js
│   └── offline.html
├── extensions/delivery-date/
│   ├── shopify.extension.toml
│   └── src/Checkout.tsx
└── docs/
    ├── AGENT_INSTRUCTIONS.md
    ├── LEGAL_FIREWALL.md
    └── DEPLOY.md
```

---

VERIFICATION CHECKLIST AFTER UNZIP

```bash
[ ] unzip sharedrop-trading-v7.1.zip
[ ] cd sharedrop-trading-v7.1
[ ] npm install          # succeeds
[ ] npx prisma generate  # succeeds
[ ] grep -r "assertNonCustody" app/   # finds payments.server.ts + app.payments.tsx
[ ] grep -r "71086287240" app/        # finds payments.server.ts + app.payments.tsx
[ ] grep -r "GY-HUG" app/routes/      # finds driving-school + gameplay
[ ] ls app/routes/       # shows 9+ routes
```

Everything from all previous responses is bundled: GLIE V7.0, VIP drivers, safety preferences, GY-HUG driving school, gameplay flow, V7.1 non-custody payments, service worker, checkout extension, and deployment configs. Run the script and you have your zip.
stdout
Standard output is empty