2026-04-16-3 add secur ,billing ,documents ,
This commit is contained in:
@@ -185,7 +185,7 @@ export class AuthService {
|
||||
} else {
|
||||
// 3. Create new if neither found
|
||||
this.logger.log(`Creating new tenant for Firebase user: ${email} (${uid})`);
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com';
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com' || email === 'hamzaayedpython@gmail.com';
|
||||
tenant = await this.tenantRepository.save({
|
||||
firebaseUid: uid,
|
||||
email,
|
||||
@@ -197,7 +197,7 @@ export class AuthService {
|
||||
}
|
||||
} else {
|
||||
// Auto-upgrade admins if they exist but are on lower plan
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com';
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com' || email === 'hamzaayedpython@gmail.com';
|
||||
if (isAdmin && tenant.plan !== TenantPlan.ENTERPRISE) {
|
||||
tenant.plan = TenantPlan.ENTERPRISE;
|
||||
await this.tenantRepository.save(tenant);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ApiKey } from './api-key.entity';
|
||||
|
||||
export enum TenantPlan {
|
||||
FREE = 'FREE',
|
||||
STARTER = 'STARTER',
|
||||
PRO = 'PRO',
|
||||
ENTERPRISE = 'ENTERPRISE',
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Post, Body, Param, UseGuards, Req } from '@nestjs/common';
|
||||
import { Controller, Get, Post, Body, Param, UseGuards, Req, ForbiddenException } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { CreateKeyDto } from './dto/management/create-key.dto';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
@@ -17,6 +17,10 @@ export class TenantController {
|
||||
@Req() req: any,
|
||||
@Body() dto: CreateKeyDto
|
||||
) {
|
||||
if (req.user && req.user.email_verified === false) {
|
||||
throw new ForbiddenException('You must verify your email address before creating an API key.');
|
||||
}
|
||||
|
||||
const tenantId = req.tenant.id;
|
||||
return this.authService.createApiKey(
|
||||
tenantId,
|
||||
|
||||
@@ -32,7 +32,18 @@ export class BillingController {
|
||||
async checkout(@Req() req: any, @Body() body: { plan: string; provider: PaymentProvider }) {
|
||||
const tenantId = req.tenant.id;
|
||||
const plan = body.plan;
|
||||
const amount = plan === 'PRO' ? 40 : 0; // Price logic
|
||||
|
||||
// Mapping prices to plans
|
||||
const pricing = {
|
||||
'STARTER': 29,
|
||||
'PRO': 89,
|
||||
};
|
||||
|
||||
const amount = pricing[plan] || 0;
|
||||
|
||||
if (amount === 0 && plan !== 'FREE') {
|
||||
throw new BadRequestException('Invalid plan or price not configured');
|
||||
}
|
||||
|
||||
if (body.provider === PaymentProvider.PAYMOB) {
|
||||
const { paymentKey, orderId } = await this.paymobProvider.createPaymentKey(tenantId, amount, plan);
|
||||
@@ -84,7 +95,7 @@ export class BillingController {
|
||||
}
|
||||
|
||||
// Redirect back to dashboard with status
|
||||
const targetUrl = `https://map-dashbord.intaleqapp.com/dashboard.html#billing?payment_status=${query.success === 'true' ? 'success' : 'failed'}&id=${query.id}`;
|
||||
const targetUrl = `https://map-dashboard.intaleqapp.com/dashboard.html#billing?payment_status=${query.success === 'true' ? 'success' : 'failed'}&id=${query.id}`;
|
||||
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
|
||||
@@ -40,7 +40,7 @@ export class BillingService {
|
||||
sub = await this.subscriptionRepository.save({
|
||||
tenantId,
|
||||
plan: 'FREE',
|
||||
monthlyRequestLimit: 8000,
|
||||
monthlyRequestLimit: 5000,
|
||||
status: SubscriptionStatus.ACTIVE,
|
||||
});
|
||||
}
|
||||
@@ -115,9 +115,10 @@ export class BillingService {
|
||||
|
||||
// Update Subscription
|
||||
const limits = {
|
||||
[TenantPlan.FREE]: 8000,
|
||||
[TenantPlan.PRO]: 50000,
|
||||
[TenantPlan.ENTERPRISE]: 1000000,
|
||||
[TenantPlan.FREE]: 5000,
|
||||
[TenantPlan.STARTER]: 25000,
|
||||
[TenantPlan.PRO]: 100000,
|
||||
[TenantPlan.ENTERPRISE]: 500000,
|
||||
};
|
||||
|
||||
const sub = await this.getSubscription(tenantId);
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import { CanActivate, ExecutionContext, Injectable, UnauthorizedException, ForbiddenException } from '@nestjs/common';
|
||||
import { TenantPlan } from '../../auth/entities/tenant.entity';
|
||||
|
||||
@Injectable()
|
||||
export class AdminGuard implements CanActivate {
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const tenant = request['tenant']; // Populated by ApiKeyGuard
|
||||
|
||||
if (!tenant) {
|
||||
throw new UnauthorizedException('Tenant not found in request');
|
||||
}
|
||||
|
||||
if (tenant.plan !== TenantPlan.ENTERPRISE) {
|
||||
throw new ForbiddenException('Admin access required. Your tenant plan must be ENTERPRISE.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { JordanResearchService } from './jordan-research.service';
|
||||
import { AdministrativeLinkingService } from './administrative-linking.service';
|
||||
import { MapRefinementService } from './map-refinement.service';
|
||||
import { ApiKeyGuard } from '../common/guards/api-key.guard';
|
||||
import { AdminGuard } from '../common/guards/admin.guard';
|
||||
import { TenantThrottlerGuard } from '../common/guards/rate-limiter.guard';
|
||||
import { SearchQueryDto } from './dto/search-query.dto';
|
||||
import { ReverseGeocodeDto } from './dto/reverse-geocode.dto';
|
||||
@@ -47,6 +48,7 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Delete('places')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Delete a place by name or ID' })
|
||||
@ApiQuery({ name: 'name', required: false })
|
||||
@ApiQuery({ name: 'id', required: false, type: Number })
|
||||
@@ -66,12 +68,14 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Post('upsert-place')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Add or Update a location (Automated Scraper)' })
|
||||
async upsertPlace(@Body() placeData: any) {
|
||||
return this.geocodingService.upsertPlace(placeData);
|
||||
}
|
||||
|
||||
@Post('upsert-batch')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Add or Update multiple locations in bulk' })
|
||||
async upsertBatch(@Body() body: { places: any[] }) {
|
||||
return this.geocodingService.upsertBatch(body.places);
|
||||
@@ -90,6 +94,7 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Post('import-boundaries')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Import administrative boundaries from a local GeoJSON file on the server' })
|
||||
@ApiQuery({ name: 'country', required: true })
|
||||
@ApiQuery({ name: 'filePath', required: true })
|
||||
@@ -107,6 +112,7 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Post('admin/sync-neighborhoods')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Sync neighborhood points from OSM for a bbox' })
|
||||
@ApiQuery({ name: 'bbox', required: false })
|
||||
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] })
|
||||
@@ -115,6 +121,7 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Post('admin/generate-voronoi')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Generate Voronoi polygons for neighborhoods' })
|
||||
@ApiQuery({ name: 'country', required: false, enum: ['jordan', 'syria', 'egypt'] })
|
||||
async generateVoronoi(@Query('country') country?: string) {
|
||||
@@ -122,6 +129,7 @@ export class GeocodingController {
|
||||
}
|
||||
|
||||
@Post('admin/link-places')
|
||||
@UseGuards(AdminGuard)
|
||||
@ApiOperation({ summary: 'Link places to administrative hierarchy' })
|
||||
@ApiQuery({ name: 'country', required: true, enum: ['jordan', 'syria', 'egypt'] })
|
||||
async linkPlaces(@Query('country') country: 'jordan' | 'syria' | 'egypt') {
|
||||
|
||||
@@ -10,6 +10,15 @@ async function bootstrap() {
|
||||
logger: ['error', 'warn', 'log', 'debug', 'verbose'],
|
||||
});
|
||||
|
||||
// Trust proxy for correct rate limiting behind Nginx / Cloudflare
|
||||
const httpAdapter = app.getHttpAdapter();
|
||||
if (httpAdapter && httpAdapter.getInstance && typeof httpAdapter.getInstance().set === 'function') {
|
||||
httpAdapter.getInstance().set('trust proxy', 1);
|
||||
}
|
||||
|
||||
// Apply standard HTTP security headers
|
||||
app.use(helmet());
|
||||
|
||||
// 1. Modern Security Headers & Permissive CORS for Production Dashboard
|
||||
app.enableCors({
|
||||
origin: true, // Reflect request origin
|
||||
|
||||
@@ -18,12 +18,13 @@ export class UsageController {
|
||||
|
||||
// Limits
|
||||
const limits = {
|
||||
FREE: 8000,
|
||||
PRO: 50000,
|
||||
ENTERPRISE: 1000000,
|
||||
FREE: 5000,
|
||||
STARTER: 25000,
|
||||
PRO: 100000,
|
||||
ENTERPRISE: 500000,
|
||||
};
|
||||
|
||||
const limit = limits[tenant.plan] || 8000;
|
||||
const limit = limits[tenant.plan] || 5000;
|
||||
|
||||
return {
|
||||
...summary,
|
||||
|
||||
@@ -13,13 +13,15 @@ import { TenantPlan } from '../auth/entities/tenant.entity';
|
||||
|
||||
// Quota Limits per Plan
|
||||
const QUOTA_LIMITS: Record<TenantPlan, number> = {
|
||||
[TenantPlan.FREE]: 8000,
|
||||
[TenantPlan.PRO]: 50000,
|
||||
[TenantPlan.ENTERPRISE]: 1000000,
|
||||
[TenantPlan.FREE]: 5000,
|
||||
[TenantPlan.STARTER]: 25000,
|
||||
[TenantPlan.PRO]: 100000,
|
||||
[TenantPlan.ENTERPRISE]: 500000,
|
||||
};
|
||||
|
||||
const RATE_LIMITS: Record<TenantPlan, number> = {
|
||||
[TenantPlan.FREE]: 10,
|
||||
[TenantPlan.FREE]: 5,
|
||||
[TenantPlan.STARTER]: 100,
|
||||
[TenantPlan.PRO]: 500,
|
||||
[TenantPlan.ENTERPRISE]: 5000,
|
||||
};
|
||||
|
||||
+110
-80
@@ -107,8 +107,8 @@
|
||||
<i data-lucide="layers" class="w-10 h-10"></i>
|
||||
</div>
|
||||
|
||||
<h1 class="text-4xl font-black tracking-tight mb-3 text-gradient">Intaleq Maps</h1>
|
||||
<p class="text-slate-400 font-medium mb-10 leading-relaxed">The premium developer platform for mapping services in Jordan & Syria.</p>
|
||||
<h1 class="text-4xl font-black tracking-tight mb-3 text-gradient" data-i18n="login-title">Intaleq Maps</h1>
|
||||
<p class="text-slate-400 font-medium mb-10 leading-relaxed" data-i18n="login-subtitle">The premium developer platform for mapping services in Jordan & Syria.</p>
|
||||
|
||||
<button onclick="auth.signInWithGoogle()" class="w-full btn bg-white text-slate-950 hover:bg-slate-100 py-4 text-base font-black shadow-xl flex items-center justify-center gap-3 active:scale-[0.98] transition-all rounded-2xl">
|
||||
<svg class="w-5 h-5" viewBox="0 0 24 24">
|
||||
@@ -117,10 +117,10 @@
|
||||
<path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/>
|
||||
<path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/>
|
||||
</svg>
|
||||
Continue with Google
|
||||
<span data-i18n="login-google">Continue with Google</span>
|
||||
</button>
|
||||
|
||||
<p class="mt-8 text-[11px] text-slate-500 font-bold uppercase tracking-widest">Enterprise Ready · Secure Access</p>
|
||||
<p class="mt-8 text-[11px] text-slate-500 font-bold uppercase tracking-widest" data-i18n="login-footer">Enterprise Ready · Secure Access</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -167,8 +167,8 @@
|
||||
|
||||
<div class="p-6">
|
||||
<div class="glass p-5 rounded-2xl border border-blue-500/10 bg-gradient-to-br from-blue-600/5 to-transparent">
|
||||
<p class="text-[10px] font-black uppercase tracking-wider text-blue-400 mb-2">Beta Access</p>
|
||||
<p class="text-xs text-slate-400 leading-relaxed font-medium mb-4">You're currently on the free sandbox tier.</p>
|
||||
<p class="text-[10px] font-black uppercase tracking-wider text-blue-400 mb-2" data-i18n="side-beta">Beta Access</p>
|
||||
<p class="text-xs text-slate-400 leading-relaxed font-medium mb-4" data-i18n="side-free-msg">You're currently on the free sandbox tier.</p>
|
||||
<a href="#billing" class="w-full btn btn-primary !py-2 !text-xs text-center flex items-center justify-center" data-i18n="side-upgrade">Upgrade Plan</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -190,7 +190,7 @@
|
||||
</button>
|
||||
|
||||
<div class="flex flex-col items-end">
|
||||
<p class="text-sm font-bold" id="tenant-name">Loading...</p>
|
||||
<p class="text-sm font-bold" id="tenant-name" data-i18n="loading">Loading...</p>
|
||||
<p class="text-[10px] text-slate-500 font-medium" id="tenant-email">developer@intaleq.com</p>
|
||||
</div>
|
||||
<div class="w-10 h-10 rounded-full bg-slate-900 border border-slate-800 flex items-center justify-center text-blue-400">
|
||||
@@ -204,8 +204,8 @@
|
||||
<!-- Home Section -->
|
||||
<section id="home" class="page-section">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-4xl text-gradient mb-2" id="welcome-msg">Welcome back...</h2>
|
||||
<p class="text-slate-400">Everything you need to build with premium Jordan Map Platform API</p>
|
||||
<h2 class="text-4xl text-gradient mb-2" id="welcome-msg" data-i18n="welcome">Welcome back...</h2>
|
||||
<p class="text-slate-400" data-i18n="tagline">Everything you need to build with premium Jordan Map Platform API</p>
|
||||
</div>
|
||||
|
||||
<!-- KPI Stats -->
|
||||
@@ -235,7 +235,7 @@
|
||||
<div class="w-10 h-10 rounded-xl bg-slate-900 flex items-center justify-center text-cyan-400">
|
||||
<i data-lucide="activity" class="w-5 h-5"></i>
|
||||
</div>
|
||||
<span class="text-xs font-bold px-2 py-1 rounded-full bg-slate-900 text-slate-500">Live</span>
|
||||
<span class="text-xs font-bold px-2 py-1 rounded-full bg-slate-900 text-slate-500" data-i18n="live">Live</span>
|
||||
</div>
|
||||
<p class="text-sm text-slate-500 font-medium mb-1" data-i18n="kpi-active-keys">Active Keys</p>
|
||||
<div class="text-2xl font-bold tracking-tight" id="active-keys-count">...</div>
|
||||
@@ -262,7 +262,7 @@
|
||||
<div class="w-full h-2 bg-slate-900 rounded-full overflow-hidden mb-2">
|
||||
<div id="usage-progress-bar" class="h-full bg-blue-500 transition-all duration-1000" style="width: 0%"></div>
|
||||
</div>
|
||||
<p class="text-[10px] text-slate-500 font-bold" id="usage-limit-text">... requests left</p>
|
||||
<p class="text-[10px] text-slate-500 font-bold" id="usage-limit-text">... <span data-i18n="req-left">requests left</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -272,10 +272,10 @@
|
||||
<div class="flex items-center justify-between mb-8">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold" data-i18n="chart-request-volume">Request Traffic</h3>
|
||||
<p class="text-sm text-slate-500">Live traffic across all API endpoints</p>
|
||||
<p class="text-sm text-slate-500" data-i18n="chart-subtitle">Live traffic across all API endpoints</p>
|
||||
</div>
|
||||
<a href="#analytics" class="text-xs text-blue-500 hover:text-blue-400 font-bold flex items-center gap-1 transition-colors">
|
||||
Full Analytics <i data-lucide="arrow-right" class="w-3.5 h-3.5"></i>
|
||||
<span data-i18n="full-analytics">Full Analytics</span> <i data-lucide="arrow-right" class="w-3.5 h-3.5"></i>
|
||||
</a>
|
||||
</div>
|
||||
<div class="h-64 flex items-end gap-1.5 px-2 relative" id="traffic-bars">
|
||||
@@ -285,20 +285,20 @@
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="glass rounded-2xl p-6 bg-gradient-to-br from-blue-600/10 to-transparent border-blue-500/10">
|
||||
<h3 class="text-lg font-bold mb-2">Quick Start</h3>
|
||||
<p class="text-sm text-slate-500 mb-6 font-medium">Get started with our lightweight SDK in seconds.</p>
|
||||
<h3 class="text-lg font-bold mb-2" data-i18n="quick-start">Quick Start</h3>
|
||||
<p class="text-sm text-slate-500 mb-6 font-medium" data-i18n="quick-start-msg">Get started with our lightweight SDK in seconds.</p>
|
||||
<div class="space-y-4">
|
||||
<div class="bg-slate-950 rounded-xl p-4 border border-slate-800 font-mono text-xs">
|
||||
<p class="text-slate-500 mb-2"># Install with npm</p>
|
||||
<p class="text-slate-500 mb-2" data-i18n="install-npm"># Install with npm</p>
|
||||
<p class="text-blue-400">npm <span class="text-slate-200">install @intaleq/maps-gl</span></p>
|
||||
</div>
|
||||
<button class="w-full btn btn-secondary text-sm group" onclick="window.open('/api/docs', '_blank')">
|
||||
<i data-lucide="terminal" class="w-4 h-4 text-blue-400 group-hover:scale-110 transition-transform"></i>
|
||||
View API Reference
|
||||
<span data-i18n="view-api-ref">View API Reference</span>
|
||||
</button>
|
||||
<a href="#playground" class="w-full btn btn-secondary text-sm group">
|
||||
<i data-lucide="globe" class="w-4 h-4 text-cyan-400 group-hover:scale-110 transition-transform"></i>
|
||||
Try Maps Playground
|
||||
<span data-i18n="try-playground">Try Maps Playground</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -308,12 +308,12 @@
|
||||
<div class="glass rounded-2xl overflow-hidden mb-12">
|
||||
<div class="p-6 border-b border-slate-800 flex items-center justify-between bg-white/[0.02]">
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">Your API Keys</h3>
|
||||
<p class="text-sm text-slate-500">Manage keys for your applications</p>
|
||||
<h3 class="text-lg font-bold" data-i18n="keys-title">Your API Keys</h3>
|
||||
<p class="text-sm text-slate-500" data-i18n="keys-subtitle">Manage keys for your applications</p>
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="app.toggleModal('create-key-modal', true)" id="create-key-btn">
|
||||
<i data-lucide="plus" class="w-4 h-4"></i>
|
||||
Create New Key
|
||||
<span data-i18n="keys-create">Create New Key</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -321,23 +321,21 @@
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr class="text-xs uppercase tracking-widest text-slate-500 bg-slate-900/40">
|
||||
<th class="px-6 py-4 font-black">Name</th>
|
||||
<th class="px-6 py-4 font-black">API Key</th>
|
||||
<th class="px-6 py-4 font-black">Status</th>
|
||||
<th class="px-6 py-4 font-black">Restrictions</th>
|
||||
<th class="px-6 py-4 font-black text-right">Actions</th>
|
||||
<th class="px-6 py-4 font-black" data-i18n="table-name">Name</th>
|
||||
<th class="px-6 py-4 font-black" data-i18n="table-key">API Key</th>
|
||||
<th class="px-6 py-4 font-black" data-i18n="table-status">Status</th>
|
||||
<th class="px-6 py-4 font-black" data-i18n="table-restrictions">Restrictions</th>
|
||||
<th class="px-6 py-4 font-black text-right" data-i18n="table-actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="keys-table-body" class="divide-y divide-slate-800/50">
|
||||
<!-- Injected by JS -->
|
||||
<tr>
|
||||
<td colspan="5" class="py-20 text-center text-slate-500">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<i data-lucide="loader-2" class="w-8 h-8 animate-spin text-blue-500"></i>
|
||||
<p>Fetching your secure keys...</p>
|
||||
<p data-i18n="keys-fetching">Fetching your secure keys...</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -347,22 +345,22 @@
|
||||
<!-- Playground Section -->
|
||||
<section id="playground" class="page-section">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-4xl text-gradient mb-2">Maps Playground</h2>
|
||||
<p class="text-slate-400">Test your API keys and visualize vector tiles in real-time</p>
|
||||
<h2 class="text-4xl text-gradient mb-2" data-i18n="side-playground">Maps Playground</h2>
|
||||
<p class="text-slate-400" data-i18n="playground-subtitle">Test your API keys and visualize vector tiles in real-time</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
|
||||
<!-- Sidebar Controls -->
|
||||
<div class="lg:col-span-1 space-y-6">
|
||||
<div class="glass p-6 rounded-2xl">
|
||||
<h4 class="text-[10px] uppercase font-black tracking-widest text-slate-500 mb-4">Configuration</h4>
|
||||
<h4 class="text-[10px] uppercase font-black tracking-widest text-slate-500 mb-4" data-i18n="pg-config">Configuration</h4>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="text-xs text-slate-400 mb-2 block">Active API Key</label>
|
||||
<label class="text-xs text-slate-400 mb-2 block" data-i18n="pg-active-key">Active API Key</label>
|
||||
<select id="pg-key-select" class="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="text-xs text-slate-400 mb-2 block">Map Style</label>
|
||||
<label class="text-xs text-slate-400 mb-2 block" data-i18n="pg-style">Map Style</label>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="playground.setStyle('obsidian')" id="style-obsidian" class="flex-1 py-3 text-xs font-bold rounded-xl bg-blue-600 text-white shadow-lg shadow-blue-500/20">Obsidian</button>
|
||||
<button onclick="playground.setStyle('light')" id="style-light" class="flex-1 py-3 text-xs font-bold rounded-xl bg-slate-900 text-slate-500">Light</button>
|
||||
@@ -379,7 +377,7 @@
|
||||
<div class="absolute top-6 left-6 w-full max-w-sm">
|
||||
<div class="relative">
|
||||
<i data-lucide="search" class="absolute left-4 top-1/2 -translate-y-1/2 text-slate-500 w-4 h-4"></i>
|
||||
<input type="text" id="pg-search" placeholder="Search Amman, Jordan..."
|
||||
<input type="text" id="pg-search" data-i18n-placeholder="pg-search-placeholder" placeholder="Search Amman, Jordan..."
|
||||
class="w-full bg-slate-950/80 backdrop-blur-md border border-slate-800 rounded-2xl px-12 py-4 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/50 shadow-2xl">
|
||||
</div>
|
||||
</div>
|
||||
@@ -391,7 +389,7 @@
|
||||
<section id="analytics" class="page-section">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-4xl text-gradient mb-2" data-i18n="side-analytics">Analytics</h2>
|
||||
<p class="text-slate-400">Deep insights into your API performance</p>
|
||||
<p class="text-slate-400" data-i18n="analytics-subtitle">Deep insights into your API performance</p>
|
||||
</div>
|
||||
<!-- Simple Chart Mockups -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-12">
|
||||
@@ -426,7 +424,7 @@
|
||||
<section id="refinement" class="page-section">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-4xl text-gradient mb-2" data-i18n="side-audit">Place Audit</h2>
|
||||
<p class="text-slate-400">Review and approve user-suggested map locations</p>
|
||||
<p class="text-slate-400" data-i18n="audit-subtitle">Review and approve user-suggested map locations</p>
|
||||
</div>
|
||||
|
||||
<div class="glass rounded-3xl overflow-hidden">
|
||||
@@ -434,21 +432,19 @@
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr class="text-xs uppercase tracking-widest text-slate-500 bg-slate-900/40">
|
||||
<th class="px-8 py-5 font-black">Location Name</th>
|
||||
<th class="px-8 py-5 font-black">Automated Match</th>
|
||||
<th class="px-8 py-5 font-black">Spatial Context</th>
|
||||
<th class="px-8 py-5 font-black text-right">Actions</th>
|
||||
<th class="px-8 py-5 font-black" data-i18n="table-loc-name">Location Name</th>
|
||||
<th class="px-8 py-5 font-black" data-i18n="table-match">Automated Match</th>
|
||||
<th class="px-8 py-5 font-black" data-i18n="table-spatial">Spatial Context</th>
|
||||
<th class="px-8 py-5 font-black text-right" data-i18n="table-actions">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="refinement-table-body" class="divide-y divide-slate-800/50">
|
||||
<tr>
|
||||
<td colspan="5" class="py-20 text-center text-slate-500">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<i data-lucide="map-pin" class="w-8 h-8 opacity-20"></i>
|
||||
<p>No pending location suggestions.</p>
|
||||
<p data-i18n="audit-empty">No pending location suggestions.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -459,7 +455,7 @@
|
||||
<section id="billing" class="page-section p-10">
|
||||
<div class="mb-12">
|
||||
<h2 class="text-3xl font-black mb-2 text-gradient" data-i18n="side-billing">Subscription & Billing</h2>
|
||||
<p class="text-slate-400 font-medium">Manage your plan, payment methods, and invoice history.</p>
|
||||
<p class="text-slate-400 font-medium" data-i18n="billing-subtitle">Manage your plan, payment methods, and invoice history.</p>
|
||||
</div>
|
||||
|
||||
<!-- Current Plan Summary -->
|
||||
@@ -469,8 +465,8 @@
|
||||
<i data-lucide="crown" class="w-8 h-8"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold mb-1">Current Plan: <span class="current-plan-name text-blue-400">FREE</span></h3>
|
||||
<p class="text-slate-500 text-sm font-medium">Your monthly usage resets on the 1st of each month.</p>
|
||||
<h3 class="text-xl font-bold mb-1"><span data-i18n="current-plan">Current Plan</span>: <span class="current-plan-name text-blue-400">FREE</span></h3>
|
||||
<p class="text-slate-500 text-sm font-medium" data-i18n="billing-reset-msg">Your monthly usage resets on the 1st of each month.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex gap-3">
|
||||
@@ -481,50 +477,79 @@
|
||||
</div>
|
||||
|
||||
<!-- Pricing Cards -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 mb-12">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-12">
|
||||
<!-- Free -->
|
||||
<div id="plan-card-free" class="glass p-8 rounded-[2.5rem] border-white/5 flex flex-col hover:border-white/10 transition-all">
|
||||
<div class="mb-8">
|
||||
<span class="text-xs font-black tracking-widest text-slate-500 uppercase">Starter</span>
|
||||
<h4 class="text-4xl font-black mt-2">$0 <span class="text-sm font-medium text-slate-500">/mo</span></h4>
|
||||
<span class="text-xs font-black tracking-widest text-slate-500 uppercase" data-i18n="plan-sandbox">Sandbox</span>
|
||||
<h4 class="text-4xl font-black mt-2">$0 <span class="text-sm font-medium text-slate-500">/<span data-i18n="mo">mo</span></span></h4>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-10 flex-1">
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> 8,000 requests /mo
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> 5,000 <span data-i18n="req-mo">requests /mo</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-blue-400 font-bold italic">
|
||||
<i data-lucide="zap" class="w-4 h-4 text-blue-400"></i> 5 <span data-i18n="req-min">requests / min</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> Standard Map Tiles
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> <span data-i18n="feature-tiles">Standard Map Tiles</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> Basic Geocoding
|
||||
<i data-lucide="x" class="w-4 h-4 text-slate-600"></i> <span data-i18n="feature-no-routing">No Routing API</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="plan-btn w-full btn bg-slate-800 text-white py-4 rounded-2xl font-black text-sm active:scale-95 transition-all">Current Plan</button>
|
||||
<button class="plan-btn w-full btn bg-slate-800 text-white py-4 rounded-2xl font-black text-sm active:scale-95 transition-all" data-i18n="plan-current">Current Plan</button>
|
||||
</div>
|
||||
|
||||
<!-- Starter -->
|
||||
<div id="plan-card-starter" class="glass p-8 rounded-[2.5rem] border-white/5 flex flex-col hover:border-white/10 transition-all bg-gradient-to-br from-emerald-500/[0.02] to-transparent">
|
||||
<div class="mb-8">
|
||||
<span class="text-xs font-black tracking-widest text-emerald-500 uppercase" data-i18n="plan-starter-title">Starter</span>
|
||||
<h4 class="text-4xl font-black mt-2">$29 <span class="text-sm font-medium text-slate-500">/<span data-i18n="mo">mo</span></span></h4>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-10 flex-1">
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> 25,000 <span data-i18n="req-mo">requests /mo</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-emerald-400 font-bold italic">
|
||||
<i data-lucide="zap" class="w-4 h-4 text-emerald-400"></i> 100 <span data-i18n="req-min">requests / min</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> <span data-i18n="feature-3d">3D Building Data</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-emerald-400"></i> <span data-i18n="feature-basic-geo">Basic Geocoding</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button onclick="billing.startCheckout('STARTER', 'PAYMOB')" class="plan-btn w-full btn bg-emerald-600 hover:bg-emerald-500 text-white py-4 rounded-2xl font-black text-sm active:scale-95 transition-all shadow-lg shadow-emerald-500/10" data-i18n="plan-upgrade-starter">Upgrade to Starter</button>
|
||||
</div>
|
||||
|
||||
<!-- Pro -->
|
||||
<div id="plan-card-pro" class="glass p-8 rounded-[2.5rem] border-blue-500/20 bg-blue-500/[0.02] flex flex-col relative overflow-hidden group">
|
||||
<div class="absolute top-4 right-4 bg-blue-500 text-white text-[10px] font-black px-3 py-1 rounded-full uppercase tracking-widest shadow-lg shadow-blue-500/20">Popular</div>
|
||||
<div class="absolute top-4 right-4 bg-blue-500 text-white text-[10px] font-black px-3 py-1 rounded-full uppercase tracking-widest shadow-lg shadow-blue-500/20" data-i18n="popular">Best Value</div>
|
||||
<div class="mb-8">
|
||||
<span class="text-xs font-black tracking-widest text-blue-400 uppercase">Professional</span>
|
||||
<h4 class="text-4xl font-black mt-2 text-gradient">$40 <span class="text-sm font-medium text-slate-500">/mo</span></h4>
|
||||
<span class="text-xs font-black tracking-widest text-blue-400 uppercase" data-i18n="plan-pro-title">Professional</span>
|
||||
<h4 class="text-4xl font-black mt-2 text-gradient">$89 <span class="text-sm font-medium text-slate-500">/<span data-i18n="mo">mo</span></span></h4>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-10 flex-1">
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> 50,000 requests /mo
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> 100,000 <span data-i18n="req-mo">requests /mo</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-blue-400 font-bold italic">
|
||||
<i data-lucide="zap" class="w-4 h-4 text-blue-400"></i> 500 <span data-i18n="req-min">requests / min</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> 3D Building Extrusion
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> <span data-i18n="feature-3d-ext">3D Building Extrusion</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> Advanced Routing API
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> <span data-i18n="feature-routing">Advanced Routing API</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> Priority Support
|
||||
<i data-lucide="check" class="w-4 h-4 text-blue-400"></i> <span data-i18n="feature-priority">Priority Support</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="flex gap-2">
|
||||
<button onclick="billing.startCheckout('PRO', 'PAYMOB')" class="plan-btn flex-1 btn bg-blue-600 hover:bg-blue-500 text-white py-4 rounded-2xl font-black text-sm shadow-xl shadow-blue-500/20 active:scale-95 transition-all">Pay with Card</button>
|
||||
<button onclick="billing.startCheckout('PRO', 'PAYMOB')" class="plan-btn flex-1 btn bg-blue-600 hover:bg-blue-500 text-white py-4 rounded-2xl font-black text-sm shadow-xl shadow-blue-500/20 active:scale-95 transition-all" data-i18n="pay-card">Pay with Card</button>
|
||||
<button onclick="billing.startCheckout('PRO', 'BINANCE')" class="p-4 rounded-2xl bg-white/5 border border-white/5 hover:bg-white/10 transition-all text-yellow-500" title="Pay with Crypto">
|
||||
<i data-lucide="bitcoin" class="w-5 h-5"></i>
|
||||
</button>
|
||||
@@ -534,24 +559,27 @@
|
||||
<!-- Enterprise -->
|
||||
<div id="plan-card-enterprise" class="glass p-8 rounded-[2.5rem] border-white/5 flex flex-col hover:border-white/10 transition-all">
|
||||
<div class="mb-8">
|
||||
<span class="text-xs font-black tracking-widest text-violet-400 uppercase">Enterprise</span>
|
||||
<h4 class="text-4xl font-black mt-2">Custom</h4>
|
||||
<span class="text-xs font-black tracking-widest text-violet-400 uppercase" data-i18n="plan-ent-title">Enterprise</span>
|
||||
<h4 class="text-4xl font-black mt-2">$299 <span class="text-sm font-medium text-slate-500">/<span data-i18n="mo">mo</span></span></h4>
|
||||
</div>
|
||||
<ul class="space-y-4 mb-10 flex-1">
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> Unlimited Requests
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> 500,000 <span data-i18n="req-mo">requests /mo</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-violet-400 font-bold italic">
|
||||
<i data-lucide="zap" class="w-4 h-4 text-violet-400"></i> 5,000 <span data-i18n="req-min">requests / min</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> Custom Data Layers
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> <span data-i18n="feature-custom">Custom Data Layers</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> SLA Guarantees
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> <span data-i18n="feature-sla">SLA Guarantees</span>
|
||||
</li>
|
||||
<li class="flex items-center gap-3 text-sm text-slate-300 font-medium">
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> Dedicated Architect
|
||||
<i data-lucide="check" class="w-4 h-4 text-violet-400"></i> <span data-i18n="feature-dedicated">Dedicated Architect</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="plan-btn w-full btn border border-violet-500/30 text-violet-300 hover:bg-violet-500/10 py-4 rounded-2xl font-black text-sm active:scale-95 transition-all">Contact Sales</button>
|
||||
<button class="plan-btn w-full btn border border-violet-500/30 text-violet-300 hover:bg-violet-500/10 py-4 rounded-2xl font-black text-sm active:scale-95 transition-all" data-i18n="contact-sales">Contact Sales</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -565,10 +593,10 @@
|
||||
<table class="w-full text-left">
|
||||
<thead>
|
||||
<tr class="bg-white/[0.01]">
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500">Date</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500">Amount</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500">Provider</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500">Status</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500" data-i18n="table-date">Date</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500" data-i18n="table-amount">Amount</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500" data-i18n="table-provider">Provider</th>
|
||||
<th class="px-8 py-5 text-[10px] font-black uppercase tracking-widest text-slate-500" data-i18n="table-status">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="invoice-table-body">
|
||||
@@ -577,13 +605,15 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Documentation Section -->
|
||||
<section id="docs" class="page-section h-full">
|
||||
<div class="flex h-full gap-8">
|
||||
<!-- Docs Nav -->
|
||||
<div class="w-64 flex flex-col gap-2 shrink-0">
|
||||
<div class="mb-4">
|
||||
<h2 class="text-xl font-black text-gradient">Guides</h2>
|
||||
<h2 class="text-xl font-black text-gradient" data-i18n="guides-title">Guides</h2>
|
||||
</div>
|
||||
<a href="javascript:void(0)" data-section="getting-started" class="docs-nav-link active bg-blue-500/10 text-blue-400 p-4 rounded-2xl text-sm font-bold flex items-center gap-3 transition-all hover:bg-blue-500/5">
|
||||
<i data-lucide="rocket" class="w-4 h-4"></i> Getting Started
|
||||
@@ -613,20 +643,20 @@
|
||||
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm" onclick="app.toggleModal('create-key-modal', false)"></div>
|
||||
<div class="absolute inset-0 flex items-center justify-center p-4">
|
||||
<div class="glass w-full max-w-md p-8 rounded-3xl animate-in fade-in zoom-in duration-300">
|
||||
<h2 class="text-2xl font-bold mb-2">Create API Key</h2>
|
||||
<p class="text-sm text-slate-500 mb-8">Set up a new access point for your application.</p>
|
||||
<h2 class="text-2xl font-bold mb-2" data-i18n="modal-key-title">Create API Key</h2>
|
||||
<p class="text-sm text-slate-500 mb-8" data-i18n="modal-key-subtitle">Set up a new access point for your application.</p>
|
||||
<form id="create-key-form" class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-xs font-black uppercase tracking-widest text-slate-500 mb-2">Key Name</label>
|
||||
<label class="block text-xs font-black uppercase tracking-widest text-slate-500 mb-2" data-i18n="modal-key-label">Key Name</label>
|
||||
<input type="text" id="new-key-name" placeholder="e.g. Production Web App" required
|
||||
class="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500/20">
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3 pt-4 border-t border-slate-800">
|
||||
<button type="button" onclick="app.toggleModal('create-key-modal', false)" class="flex-1 btn btn-secondary">
|
||||
<button type="button" onclick="app.toggleModal('create-key-modal', false)" class="flex-1 btn btn-secondary" data-i18n="cancel">
|
||||
Cancel
|
||||
</button>
|
||||
<button type="submit" id="btn-submit-key" class="flex-1 btn btn-primary">
|
||||
<button type="submit" id="btn-submit-key" class="flex-1 btn btn-primary" data-i18n="keys-create">
|
||||
Create Key
|
||||
</button>
|
||||
</div>
|
||||
@@ -638,11 +668,11 @@
|
||||
<!-- core script -->
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/auth.js"></script>
|
||||
<script src="js/docs.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/playground.js"></script>
|
||||
<script src="js/analytics.js"></script>
|
||||
<script src="js/billing.js"></script>
|
||||
<script src="js/refinement.js"></script>
|
||||
<script src="js/docs.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -19,6 +19,9 @@ const app = {
|
||||
// Check for payment success/fail status earlier
|
||||
app.checkPaymentStatus();
|
||||
|
||||
// Trigger initial routing based on URL hash
|
||||
app.handleRouting();
|
||||
|
||||
if (window.lucide) lucide.createIcons();
|
||||
} catch (e) {
|
||||
console.error('Core UI Init failed', e);
|
||||
|
||||
@@ -47,7 +47,7 @@ const billing = {
|
||||
document.querySelectorAll('.current-plan-name').forEach(el => el.textContent = sub.plan);
|
||||
|
||||
// 2. Render Plan Cards logic
|
||||
const plans = ['FREE', 'PRO', 'ENTERPRISE'];
|
||||
const plans = ['FREE', 'STARTER', 'PRO', 'ENTERPRISE'];
|
||||
plans.forEach(p => {
|
||||
const card = document.getElementById(`plan-card-${p.toLowerCase()}`);
|
||||
if (card) {
|
||||
|
||||
+300
-87
@@ -1,23 +1,28 @@
|
||||
/**
|
||||
* Documentation Engine for Intaleq Dashboard
|
||||
* Comprehensive API Reference (EN/AR)
|
||||
* Updated with Premium Visuals & High-Performance Examples
|
||||
*/
|
||||
|
||||
const docs = {
|
||||
init: () => {
|
||||
console.log('📚 Initializing Documentation...');
|
||||
console.log('📚 Intaleq Documentation Module Init');
|
||||
docs.bindEvents();
|
||||
docs.renderSection('getting-started');
|
||||
|
||||
// Initial render based on existing active links or default
|
||||
const activeLink = document.querySelector('.docs-nav-link.active');
|
||||
const section = activeLink ? activeLink.getAttribute('data-section') : 'getting-started';
|
||||
docs.renderSection(section);
|
||||
},
|
||||
|
||||
bindEvents: () => {
|
||||
// Handle side-nav clicks
|
||||
document.querySelectorAll('.docs-nav-link').forEach(link => {
|
||||
link.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
const section = e.currentTarget.getAttribute('data-section');
|
||||
docs.renderSection(section);
|
||||
|
||||
// Active state
|
||||
// Active state management
|
||||
document.querySelectorAll('.docs-nav-link').forEach(l => l.classList.remove('active', 'bg-blue-500/10', 'text-blue-400'));
|
||||
e.currentTarget.classList.add('active', 'bg-blue-500/10', 'text-blue-400');
|
||||
});
|
||||
@@ -28,68 +33,163 @@ const docs = {
|
||||
const container = document.getElementById('docs-content');
|
||||
if (!container) return;
|
||||
|
||||
// Content repository
|
||||
const lang = i18n.currentLang || 'en';
|
||||
const isAr = lang === 'ar';
|
||||
|
||||
const content = {
|
||||
'getting-started': `
|
||||
<div class="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<div>
|
||||
<h3 class="text-3xl font-black mb-4">Getting Started</h3>
|
||||
<p class="text-slate-400 leading-relaxed">Welcome to the Intaleq Map Platform. Our APIs allow you to integrate high-quality vector maps, geocoding, and routing into your web and mobile applications with ease.</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div class="glass p-6 rounded-2xl border-white/5">
|
||||
<h4 class="font-bold mb-2 flex items-center gap-2">
|
||||
<i data-lucide="key" class="w-4 h-4 text-blue-400"></i>
|
||||
1. Get an API Key
|
||||
</h4>
|
||||
<p class="text-xs text-slate-500">Go to the Credentials page and create your first API key.</p>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-2xl border-white/5">
|
||||
<h4 class="font-bold mb-2 flex items-center gap-2">
|
||||
<i data-lucide="code" class="w-4 h-4 text-emerald-400"></i>
|
||||
2. Install SDK
|
||||
</h4>
|
||||
<p class="text-xs text-slate-500">Use our MapLibre wrappers for JavaScript or Flutter.</p>
|
||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||
<div class="relative overflow-hidden rounded-[3rem] p-12 bg-gradient-to-br from-blue-600/20 via-blue-500/5 to-transparent border border-white/10 group">
|
||||
<div class="absolute -top-24 -right-24 w-96 h-96 bg-blue-500/10 blur-[120px] rounded-full group-hover:bg-blue-500/20 transition-all duration-700"></div>
|
||||
<div class="relative z-10">
|
||||
<h3 class="text-5xl font-black mb-6 text-gradient">${isAr ? 'انطلق في ثوانٍ' : 'Launch in Seconds'}</h3>
|
||||
<p class="text-slate-300 text-xl leading-relaxed max-w-2xl">
|
||||
${isAr ? 'مرحباً بك في مستقبل الخرائط في المنطقة. توفر لك منصة "انطلاق" واجهات برمجية ذكية، خرائط Vector فائقة الدقة، ومباني ثلاثية الأبعاد متكاملة.' : 'Welcome to the future of regional mapping. Intaleq provides high-fidelity vector tiles, intelligent geocoding, and native 3D building support for Jordan & Syria.'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<h4 class="text-lg font-bold">Base URL</h4>
|
||||
<div class="bg-slate-900 rounded-xl p-4 font-mono text-sm border border-slate-800 text-blue-400">
|
||||
https://map-dashbord.intaleqapp.com/api
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-blue-500/30 transition-all group">
|
||||
<div class="w-16 h-16 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">01</div>
|
||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'مفتاح الوصول' : 'Access Key'}</h4>
|
||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'قم بإنشاء مفتاح API من لوحة التحكم لتفعيل طلباتك.' : 'Generate your secure API key from the dashboard to authenticate requests.'}</p>
|
||||
</div>
|
||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-emerald-500/30 transition-all group">
|
||||
<div class="w-16 h-16 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">02</div>
|
||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'تكامل الخريطة' : 'Map Integration'}</h4>
|
||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'اختر النمط (Obsidian أو Light) وادمج الخريطة في تطبيقك.' : 'Select a theme and integrate the vector tiles using our GL styles.'}</p>
|
||||
</div>
|
||||
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-violet-500/30 transition-all group">
|
||||
<div class="w-16 h-16 rounded-2xl bg-violet-500/10 flex items-center justify-center text-violet-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">03</div>
|
||||
<h4 class="font-black text-2xl mb-4">${isAr ? 'بيانات ذكية' : 'Smart Data'}</h4>
|
||||
<p class="text-slate-400 leading-relaxed">${isAr ? 'استخدم خدمات البحث والتوجيه لإضافة ذكاء مكاني لتطبيقك.' : 'Leverage Geocoding and Routing APIs for advanced spatial intelligence.'}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8 pt-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="h-8 w-1.5 bg-blue-500 rounded-full"></div>
|
||||
<h4 class="text-3xl font-black">${isAr ? 'بيانات الوصول والمصادقة' : 'Domain & Authentication'}</h4>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 relative group overflow-hidden shadow-2xl">
|
||||
<div class="absolute top-0 right-0 p-6 opacity-20 group-hover:opacity-100 transition-opacity">
|
||||
<span class="bg-blue-500/10 text-blue-400 px-4 py-1.5 rounded-full text-xs font-black uppercase tracking-widest">Production URL</span>
|
||||
</div>
|
||||
<code class="text-blue-400 font-mono text-2xl block mb-6 select-all">https://map-saas.intaleq.com/api</code>
|
||||
<div class="flex flex-col md:flex-row gap-8 text-slate-400">
|
||||
<div class="flex-1 space-y-2">
|
||||
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'طريقة المصادقة' : 'Auth Method'}</p>
|
||||
<p class="text-lg">${isAr ? 'يتم إرسال المفتاح عبر الـ HTTP Header التالي:' : 'Pass your API key in the following HTTP header:'}</p>
|
||||
<code class="text-blue-300 font-mono font-black text-xl">x-api-key</code>
|
||||
</div>
|
||||
<div class="flex-1 space-y-2 border-slate-800 md:border-l md:pl-8">
|
||||
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'نطاق الوصول' : 'Allowed Origins'}</p>
|
||||
<p class="text-lg">${isAr ? 'تأكد من إضافة النطاق الخاص بك في إعدادات المفتاح.' : 'Ensure your request origin is listed in the key restrictions.'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-8 pt-6">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="h-8 w-1.5 bg-emerald-500 rounded-full"></div>
|
||||
<h4 class="text-3xl font-black">${isAr ? 'حدود الاستخدام (RPM)' : 'Rate Limiting (RPM)'}</h4>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
|
||||
<div class="glass p-6 rounded-3xl border-white/5 text-center">
|
||||
<p class="text-[10px] font-black uppercase text-slate-500 mb-1">Free</p>
|
||||
<p class="text-2xl font-black text-white">5 <span class="text-[10px] text-slate-500">RPM</span></p>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-3xl border-emerald-500/20 text-center bg-emerald-500/5">
|
||||
<p class="text-[10px] font-black uppercase text-emerald-500 mb-1">Starter</p>
|
||||
<p class="text-2xl font-black text-white">100 <span class="text-[10px] text-slate-500">RPM</span></p>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-3xl border-blue-500/20 text-center bg-blue-500/5">
|
||||
<p class="text-[10px] font-black uppercase text-blue-500 mb-1">Pro</p>
|
||||
<p class="text-2xl font-black text-white">500 <span class="text-[10px] text-slate-500">RPM</span></p>
|
||||
</div>
|
||||
<div class="glass p-6 rounded-3xl border-violet-500/20 text-center bg-violet-500/5">
|
||||
<p class="text-[10px] font-black uppercase text-violet-500 mb-1">Enterprise</p>
|
||||
<p class="text-2xl font-black text-white">5000 <span class="text-[10px] text-slate-500">RPM</span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
'tiles-api': `
|
||||
<div class="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<div>
|
||||
<h3 class="text-3xl font-black mb-2">Vector Tiles API</h3>
|
||||
<p class="text-slate-400">Render high-performance vector maps from our global database.</p>
|
||||
</div>
|
||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6">
|
||||
<div>
|
||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'خرائط الـ Vector' : 'Vector Tiles API'}</h3>
|
||||
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'خرائط تفاعلية فائقة السرعة تدعم العرض ثلاثي الأبعاد والتحكم الكامل في الخصائص.' : 'High-performance interactive maps with native 3D buildings and custom GL styles.'}</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<span class="px-4 py-2 bg-emerald-500/10 text-emerald-400 rounded-2xl text-xs font-black uppercase tracking-widest border border-emerald-500/20">Active</span>
|
||||
<span class="px-4 py-2 bg-blue-500/10 text-blue-400 rounded-2xl text-xs font-black uppercase tracking-widest border border-blue-500/20">V1.2</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="endpoint-card glass rounded-2xl border-white/5 overflow-hidden">
|
||||
<div class="p-4 bg-white/[0.02] border-b border-white/5 flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="px-2 py-1 bg-green-500/20 text-green-400 text-[10px] font-black rounded uppercase">GET</span>
|
||||
<code class="text-xs font-bold">/maps/style.json</code>
|
||||
</div>
|
||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl bg-gradient-to-b from-white/[0.03] to-transparent">
|
||||
<div class="p-8 bg-white/[0.04] border-b border-white/5 flex items-center justify-between">
|
||||
<div class="flex items-center gap-6">
|
||||
<div class="px-4 py-2 bg-emerald-500 text-white text-sm font-black rounded-xl shadow-lg shadow-emerald-500/20 uppercase tracking-tighter">GET</div>
|
||||
<code class="text-lg font-bold text-slate-100 font-mono">/v1/maps/style.json</code>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<p class="text-sm text-slate-400 mb-6">Returns the MapLibre-compatible style configuration. Use the <code>theme</code> parameter to switch between 'light' and 'obsidian'.</p>
|
||||
<div class="hidden md:flex items-center gap-2 text-slate-500 text-xs font-bold italic">
|
||||
<i data-lucide="clock" class="w-3.5 h-3.5"></i> 20-40ms response
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-12">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
<div class="space-y-8">
|
||||
<div>
|
||||
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-blue-400 mb-6">${isAr ? 'المعاملات المدعومة' : 'Query Parameters'}</h5>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5 group hover:border-blue-500/20 transition-all">
|
||||
<div>
|
||||
<code class="text-blue-300 font-bold">theme</code>
|
||||
<p class="text-[10px] text-slate-500 italic mt-1">${isAr ? 'نمط الخريطة (light أو obsidian)' : 'Map visual theme (light | obsidian)'}</p>
|
||||
</div>
|
||||
<span class="text-[10px] font-mono text-slate-600 uppercase">Optional</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5 group hover:border-blue-500/20 transition-all">
|
||||
<div>
|
||||
<code class="text-blue-300 font-bold">3d</code>
|
||||
<p class="text-[10px] text-slate-500 italic mt-1">${isAr ? 'تفعيل/إلغاء المباني ثلاثية الأبعاد' : 'Enable/Disable 3D buildings'}</p>
|
||||
</div>
|
||||
<span class="text-[10px] font-mono text-slate-600 uppercase">Boolean</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h5 class="text-xs font-black uppercase tracking-widest text-slate-500 mb-4">Code Example</h5>
|
||||
<div class="relative group">
|
||||
<pre class="bg-slate-950 p-4 rounded-xl text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto">
|
||||
// Initialize MapLibre with Intaleq Style
|
||||
<div class="p-6 bg-slate-900/40 rounded-3xl border border-white/5 italic text-slate-500 text-sm leading-relaxed relative">
|
||||
<i data-lucide="info" class="absolute -top-3 -right-3 w-8 h-8 text-blue-500/20"></i>
|
||||
${isAr ? 'ملاحظة: يتم استهلاك رصيد الخرائط بناءً على عدد الـ (Tile Requests) التي يتم طلبها أثناء التنقل في الخريطة.' : 'Note: Quota is consumed per tile request. High-density 3D areas may consume more resources.'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="bg-slate-950 rounded-[2.5rem] p-8 border border-slate-800 shadow-inner group">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<span class="text-[10px] font-black uppercase text-blue-400 tracking-widest flex items-center gap-2">
|
||||
<i data-lucide="code-2" class="w-3 h-3"></i> JavaScript (MapLibre)
|
||||
</span>
|
||||
</div>
|
||||
<pre class="text-[11px] font-mono text-slate-300 leading-relaxed overflow-x-auto whitespace-pre">
|
||||
const map = new maplibregl.Map({
|
||||
container: 'map',
|
||||
style: 'https://map-dashbord.intaleqapp.com/api/maps/style.json?theme=obsidian',
|
||||
center: [35.9106, 31.9539], // Amman
|
||||
zoom: 12
|
||||
container: 'map-id',
|
||||
style: 'https://map-saas.intaleq.com/api/v1/maps/style.json?theme=obsidian',
|
||||
transformRequest: (url) => {
|
||||
return {
|
||||
url: url,
|
||||
headers: { 'x-api-key': 'YOUR_KEY_HERE' }
|
||||
}
|
||||
}
|
||||
});</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -97,50 +197,163 @@ const map = new maplibregl.Map({
|
||||
</div>
|
||||
`,
|
||||
'geocoding-api': `
|
||||
<div class="space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-500">
|
||||
<div>
|
||||
<h3 class="text-3xl font-black mb-2">Geocoding API</h3>
|
||||
<p class="text-slate-400">Convert addresses to coordinates (Forward) or coordinates to addresses (Reverse).</p>
|
||||
</div>
|
||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||
<header>
|
||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'البحث المكاني (Geocoding)' : 'Geocoding API'}</h3>
|
||||
<p class="text-slate-400 text-xl max-w-2xl">${isAr ? 'حوّل العناوين إلى إحداثيات أو العكس بدقة غير مسبوقة في الأردن وسوريا.' : 'Transform addresses into coordinates or reverse resolve locations with extreme accuracy in the Levant region.'}</p>
|
||||
</header>
|
||||
|
||||
<div class="endpoint-card glass rounded-2xl border-white/5 overflow-hidden">
|
||||
<div class="p-4 bg-white/[0.02] border-b border-white/5 flex items-center gap-3">
|
||||
<span class="px-2 py-1 bg-green-500/20 text-green-400 text-[10px] font-black rounded uppercase">GET</span>
|
||||
<code class="text-xs font-bold">/geocoding/search</code>
|
||||
<div class="grid grid-cols-1 gap-10">
|
||||
<!-- Forward Search -->
|
||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
|
||||
<div class="p-8 bg-blue-500/5 border-b border-white/5 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="px-4 py-2 bg-blue-600 text-white text-xs font-black rounded-xl shadow-lg shadow-blue-500/20 uppercase tracking-widest">SEARCH</div>
|
||||
<code class="text-base font-bold text-slate-200">/v1/geocoding/search</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-12">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
<div class="space-y-8">
|
||||
<table class="w-full text-left">
|
||||
<thead class="text-[10px] items-center text-slate-500 font-black uppercase tracking-[0.2em]">
|
||||
<tr class="border-b border-white/5">
|
||||
<th class="pb-4">${isAr ? 'البارامتر' : 'Key'}</th>
|
||||
<th class="pb-4 text-right">${isAr ? 'الوصف' : 'Description'}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
<tr class="border-b border-white/[0.02]">
|
||||
<td class="py-5 font-bold text-blue-400 font-mono text-base">q</td>
|
||||
<td class="py-5 text-right text-slate-400 italic">${isAr ? 'نص البحث (مثلاً: "عمان، الجبيهة")' : 'Query string (e.g., "Amman, Jordan")'}</td>
|
||||
</tr>
|
||||
<tr class="border-b border-white/[0.02]">
|
||||
<td class="py-5 font-bold text-blue-400 font-mono text-base">limit</td>
|
||||
<td class="py-5 text-right text-slate-400 italic">${isAr ? 'عدد النتائج (الافتراضي: 5)' : 'Max results limit'}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="bg-blue-500/5 rounded-[2rem] p-8 border border-blue-500/10">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<i data-lucide="terminal" class="w-5 h-5 text-blue-400"></i>
|
||||
<span class="text-xs font-black uppercase text-blue-400 tracking-widest">cURL Fast Access</span>
|
||||
</div>
|
||||
<code class="text-xs font-mono text-slate-300 block select-all leading-loose">
|
||||
curl "https://map-saas.intaleq.com/api/v1/geocoding/search?q=Amman" \\<br>
|
||||
-H "x-api-key: YOUR_SECURE_KEY"
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-950 rounded-[3rem] p-8 border border-slate-800 shadow-inner">
|
||||
<div class="flex justify-between items-center mb-6">
|
||||
<span class="text-[10px] font-black uppercase text-emerald-400 tracking-widest flex items-center gap-2">
|
||||
<i data-lucide="layers" class="w-3 h-3"></i> Response Structure
|
||||
</span>
|
||||
<span class="text-[10px] font-mono text-slate-600">application/json</span>
|
||||
</div>
|
||||
<pre class="text-[11px] font-mono text-emerald-300/80 leading-relaxed overflow-y-auto h-64 scrollbar-hide">
|
||||
[
|
||||
{
|
||||
"name": "Amman",
|
||||
"name_ar": "عمان",
|
||||
"lat": 31.9539,
|
||||
"lng": 35.9106,
|
||||
"district": "Capital",
|
||||
"country": "Jordan",
|
||||
"type": "city",
|
||||
"boundingbox": [31.81, 32.07, 35.72, 36.14]
|
||||
}
|
||||
]</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<table class="w-full text-left text-xs mb-6">
|
||||
<thead>
|
||||
<tr class="text-slate-500 uppercase tracking-widest font-black border-b border-white/5">
|
||||
<th class="pb-3">Parameter</th>
|
||||
<th class="pb-3">Type</th>
|
||||
<th class="pb-3">Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="divide-y divide-white/[0.02]">
|
||||
<tr>
|
||||
<td class="py-3 font-bold text-blue-400 font-mono">q</td>
|
||||
<td class="py-3 text-slate-500">string</td>
|
||||
<td class="py-3">Search query (address, place, coordinates)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-3 font-bold text-blue-400 font-mono">limit</td>
|
||||
<td class="py-3 text-slate-500">number</td>
|
||||
<td class="py-3">Max results (default: 5)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
'routing-api': `
|
||||
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
|
||||
<header class="relative p-12 overflow-hidden rounded-[3.5rem] bg-slate-900 border border-white/5 md:flex md:items-center md:justify-between shadow-2xl">
|
||||
<div class="absolute -top-24 -left-24 w-64 h-64 bg-violet-600/10 blur-[100px] rounded-full"></div>
|
||||
<div class="relative z-10">
|
||||
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'محرك التوجيه (Routing)' : 'Routing Engine'}</h3>
|
||||
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'حساب أسرع المسارات مع تحليلات لحظية لحركة المرور في مراكز المدن.' : 'Fast pathfinding with traffic-aware duration metrics for urban environments.'}</p>
|
||||
</div>
|
||||
<div class="relative z-10 mt-6 md:mt-0">
|
||||
<div class="w-32 h-32 rounded-full border-4 border-violet-500/20 flex items-center justify-center animate-pulse">
|
||||
<i data-lucide="navigation" class="w-12 h-12 text-violet-400"></i>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<h5 class="text-xs font-black uppercase tracking-widest text-slate-500 mb-4">Request</h5>
|
||||
<pre class="bg-slate-950 p-4 rounded-xl text-xs font-mono text-blue-400 mb-4">curl "https://map-dashbord.intaleqapp.com/api/geocoding/search?q=Amman&limit=1" \\
|
||||
-H "x-api-key: YOUR_API_KEY"</pre>
|
||||
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
|
||||
<div class="p-8 bg-violet-500/5 border-b border-white/5 flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="px-4 py-2 bg-violet-600 text-white text-xs font-black rounded-xl shadow-lg shadow-violet-500/20 uppercase tracking-widest">ROUTE</div>
|
||||
<code class="text-base font-bold text-slate-200">/v1/routing/route</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-12">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
|
||||
<div class="space-y-10">
|
||||
<div>
|
||||
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-violet-400 mb-8">${isAr ? 'إحداثيات المسار' : 'Waypoints & Logic'}</h5>
|
||||
<div class="grid grid-cols-2 gap-6">
|
||||
<div class="bg-indigo-950/20 p-6 rounded-[2rem] border border-white/5">
|
||||
<code class="text-violet-300 font-bold block mb-1">start</code>
|
||||
<p class="text-[10px] text-slate-500 italic">"35.91,31.95" (lng,lat)</p>
|
||||
</div>
|
||||
<div class="bg-indigo-950/20 p-6 rounded-[2rem] border border-white/5">
|
||||
<code class="text-violet-300 font-bold block mb-1">end</code>
|
||||
<p class="text-[10px] text-slate-500 italic">"35.85,31.82" (lng,lat)</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between p-5 bg-white/5 rounded-2xl border border-white/5">
|
||||
<code class="text-blue-300 font-bold">profile</code>
|
||||
<span class="text-xs text-slate-400 font-bold italic">car | bike | foot</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-5 bg-white/5 rounded-2xl border border-white/5">
|
||||
<code class="text-blue-300 font-bold">alternatives</code>
|
||||
<span class="text-xs text-slate-600 font-black uppercase">Boolean</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 shadow-inner group">
|
||||
<div class="flex justify-between items-center mb-8">
|
||||
<span class="text-[10px] font-black uppercase text-violet-400 tracking-widest flex items-center gap-2">
|
||||
<i data-lucide="activity" class="w-3.5 h-3.5"></i> Traffic Analytics JSON
|
||||
</span>
|
||||
</div>
|
||||
<pre class="text-[11px] font-mono text-violet-300/80 leading-loose overflow-x-auto h-56">
|
||||
{
|
||||
"distance": 8420.5,
|
||||
"duration": 940,
|
||||
"traffic_duration": 1120,
|
||||
"geometry": "encoded_polyline_here",
|
||||
"steps": [
|
||||
{ "instruction": "Turn right...", "distance": 200 }
|
||||
]
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
};
|
||||
|
||||
container.innerHTML = content[id] || '<p class="text-slate-500">Documentation section coming soon...</p>';
|
||||
lucide.createIcons();
|
||||
container.innerHTML = content[id] || '<div class="h-64 flex items-center justify-center italic text-slate-600">Documentation section coming soon...</div>';
|
||||
|
||||
// Re-initialize icons
|
||||
if (window.lucide) lucide.createIcons();
|
||||
}
|
||||
};
|
||||
|
||||
// Global initializer
|
||||
window.docs = docs;
|
||||
|
||||
+206
-63
@@ -1,5 +1,7 @@
|
||||
/**
|
||||
* i18n Translation Engine
|
||||
* Localization Engine for Intaleq Map SaaS
|
||||
* Supports Arabic (RTL) and English (LTR)
|
||||
* Handles both landing page and dashboard
|
||||
*/
|
||||
|
||||
const i18n = {
|
||||
@@ -7,21 +9,20 @@ const i18n = {
|
||||
|
||||
translations: {
|
||||
en: {
|
||||
// Navbar
|
||||
// Navbar (Landing)
|
||||
'nav-features': 'Features',
|
||||
'nav-why': 'Why Us?',
|
||||
'nav-pricing': 'Pricing',
|
||||
'nav-launch': 'Launch Dashboard',
|
||||
'lang-toggle': 'العربية',
|
||||
|
||||
// Hero
|
||||
// Hero (Landing)
|
||||
'hero-badge': 'Now with 3D Buildings in Jordan & Syria',
|
||||
'hero-title': 'The Map API That <br> <span class="text-blue-500">Doesn\'t Break</span> The Bank.',
|
||||
'hero-desc': 'Build premium location-based apps with high-fidelity vector tiles, optimized routing, and 3D buildings. 85% cheaper than Google Maps.',
|
||||
'hero-cta-start': 'Start Building Free',
|
||||
'hero-cta-view': 'View Comparison',
|
||||
|
||||
// Features
|
||||
// Features (Landing)
|
||||
'feat-latency-title': 'Zero Latency',
|
||||
'feat-latency-desc': 'Our infrastructure is optimized for MENA region, ensuring map tiles load in under 200ms.',
|
||||
'feat-routing-title': 'Smart Routing',
|
||||
@@ -29,7 +30,7 @@ const i18n = {
|
||||
'feat-geocoding-title': 'Local Geocoding',
|
||||
'feat-geocoding-desc': 'Highly accurate search for local landmarks and neighborhoods in Jordan & Syria.',
|
||||
|
||||
// Dashboard Sidebar
|
||||
// Sidebar (Dashboard)
|
||||
'side-dashboard': 'Dashboard',
|
||||
'side-playground': 'Playground',
|
||||
'side-analytics': 'Analytics',
|
||||
@@ -37,56 +38,123 @@ const i18n = {
|
||||
'side-audit': 'Place Audit',
|
||||
'side-docs': 'Documentation',
|
||||
'side-upgrade': 'Upgrade Plan',
|
||||
'side-beta': 'Beta Access',
|
||||
'side-free-msg': "You're currently on the sandbox tier.",
|
||||
|
||||
// Dashboard General
|
||||
'welcome': 'Welcome back',
|
||||
// Header
|
||||
'system-status': 'System Operational',
|
||||
'quota-card-title': 'Monthly Quota',
|
||||
'requests-left': 'requests left',
|
||||
'used-label': 'USED',
|
||||
'lang-toggle': 'العربية',
|
||||
'loading': 'Loading...',
|
||||
|
||||
// KPI Labels
|
||||
// Login
|
||||
'login-title': 'Intaleq Maps',
|
||||
'login-subtitle': 'The premium developer platform for mapping services in Jordan & Syria.',
|
||||
'login-google': 'Continue with Google',
|
||||
'login-footer': 'Enterprise Ready · Secure Access',
|
||||
|
||||
// Home / Dashboard
|
||||
'welcome': 'Welcome back',
|
||||
'tagline': 'Everything you need to build with premium Jordan Map Platform API',
|
||||
'kpi-total-req': 'Total Requests',
|
||||
'kpi-success-rate': 'Success Rate',
|
||||
'kpi-active-keys': 'Active Keys',
|
||||
'kpi-latency': 'Avg Latency',
|
||||
'quota-card-title': 'Monthly Quota',
|
||||
'req-left': 'requests left',
|
||||
'live': 'Live',
|
||||
'chart-request-volume': 'Request Traffic',
|
||||
'chart-subtitle': 'Live traffic across all API endpoints',
|
||||
'full-analytics': 'Full Analytics',
|
||||
'quick-start': 'Quick Start',
|
||||
'quick-start-msg': 'Get started with our lightweight SDK in seconds.',
|
||||
'install-npm': '# Install with npm',
|
||||
'view-api-ref': 'View API Reference',
|
||||
'try-playground': 'Try Maps Playground',
|
||||
|
||||
// Keys Table
|
||||
'keys-title': 'Your API Keys',
|
||||
'keys-subtitle': 'Manage keys for your applications',
|
||||
'keys-create': 'Create New Key',
|
||||
'keys-fetching': 'Fetching your secure keys...',
|
||||
'table-name': 'Name',
|
||||
'table-key': 'API Key',
|
||||
'table-status': 'Status',
|
||||
'table-restrictions': 'Restrictions',
|
||||
'table-actions': 'Actions',
|
||||
'table-loc-name': 'Location Name',
|
||||
'table-match': 'Automated Match',
|
||||
'table-spatial': 'Spatial Context',
|
||||
'table-date': 'Date',
|
||||
'table-amount': 'Amount',
|
||||
'table-provider': 'Provider',
|
||||
|
||||
// Playground
|
||||
'playground-subtitle': 'Test your API keys and visualize vector tiles in real-time',
|
||||
'pg-config': 'Configuration',
|
||||
'pg-active-key': 'Active API Key',
|
||||
'pg-style': 'Map Style',
|
||||
'pg-search-placeholder': 'Search Amman, Jordan...',
|
||||
|
||||
// Analytics
|
||||
'chart-request-volume': 'Request Volume (Overall)',
|
||||
'chart-latency-breakdown': 'Latency Breakdown (ms)',
|
||||
'analytics-subtitle': 'Deep insights into your API performance',
|
||||
'chart-success-ratio': 'Success Ratio',
|
||||
'chart-days': ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
|
||||
'chart-latency-breakdown': 'Latency Breakdown',
|
||||
|
||||
// Audit
|
||||
'audit-subtitle': 'Review and approve user-suggested map locations',
|
||||
'audit-empty': 'No pending location suggestions.',
|
||||
|
||||
// Billing
|
||||
'bill-current-plan': 'Your Current Plan',
|
||||
'bill-renewal': 'Renewal',
|
||||
'bill-usage-snapshot': 'Usage Snapshot',
|
||||
'bill-used': 'used',
|
||||
'bill-limit': 'limit',
|
||||
'bill-invoices': 'Invoice History',
|
||||
'bill-status-active': 'Active',
|
||||
'billing-subtitle': 'Manage your plan, payment methods, and invoice history.',
|
||||
'current-plan': 'Current Plan',
|
||||
'billing-reset-msg': 'Your monthly usage resets on the 1st of each month.',
|
||||
'active': 'Active',
|
||||
'plan-sandbox': 'Sandbox',
|
||||
'plan-starter-title': 'Starter',
|
||||
'plan-pro-title': 'Professional',
|
||||
'plan-ent-title': 'Enterprise',
|
||||
'popular': 'Best Value',
|
||||
'mo': 'mo',
|
||||
'req-mo': 'requests /mo',
|
||||
'req-min': 'requests / min',
|
||||
'feature-tiles': 'Standard Map Tiles',
|
||||
'feature-no-routing': 'No Routing API',
|
||||
'feature-3d': '3D Building Data',
|
||||
'feature-3d-ext': '3D Building Extrusion',
|
||||
'feature-basic-geo': 'Basic Geocoding',
|
||||
'feature-routing': 'Advanced Routing API',
|
||||
'feature-priority': 'Priority Support',
|
||||
'feature-custom': 'Custom Data Layers',
|
||||
'feature-sla': 'SLA Guarantees',
|
||||
'feature-dedicated': 'Dedicated Architect',
|
||||
'plan-current': 'Current Plan',
|
||||
'plan-upgrade-starter': 'Upgrade to Starter',
|
||||
'pay-card': 'Pay with Card',
|
||||
'contact-sales': 'Contact Sales',
|
||||
'bill-transaction-history': 'Transaction History',
|
||||
|
||||
// Place Audit
|
||||
'audit-title': 'Place Audit & Contributions',
|
||||
'audit-desc': 'Review and approve community submitted locations.',
|
||||
'audit-no-data': 'No pending contributions found.',
|
||||
// Modals
|
||||
'modal-key-title': 'Create API Key',
|
||||
'modal-key-subtitle': 'Set up a new access point for your application.',
|
||||
'modal-key-label': 'Key Name',
|
||||
'cancel': 'Cancel',
|
||||
'guides-title': 'Guides'
|
||||
},
|
||||
ar: {
|
||||
// Navbar
|
||||
// Navbar (Landing)
|
||||
'nav-features': 'المميزات',
|
||||
'nav-why': 'لماذا نحن؟',
|
||||
'nav-pricing': 'الأسعار',
|
||||
'nav-launch': 'لوحة التحكم',
|
||||
'lang-toggle': 'English',
|
||||
|
||||
// Hero
|
||||
// Hero (Landing)
|
||||
'hero-badge': 'الآن مع المباني ثلاثية الأبعاد في الأردن وسوريا',
|
||||
'hero-title': 'واجهة خرائط برمجية <br> <span class="text-blue-500">لا ترهق</span> ميزانيتك.',
|
||||
'hero-desc': 'ابنِ تطبيقات خرائط فاخرة مع خرائط مجهزة، توجيه ذكي، ومباني ثلاثية الأبعاد. أوفر بنسبة 85% من خرائط جوجل.',
|
||||
'hero-cta-start': 'ابدأ مجاناً',
|
||||
'hero-cta-view': 'قارن الأسعار',
|
||||
|
||||
// Features
|
||||
// Features (Landing)
|
||||
'feat-latency-title': 'سرعة فائقة',
|
||||
'feat-latency-desc': 'بنيتنا التحتية محسنة لمنطقة الشرق الأوسط، مما يضمن تحميل الخرائط في أقل من 200 مللي ثانية.',
|
||||
'feat-routing-title': 'توجيه ذكي',
|
||||
@@ -94,59 +162,127 @@ const i18n = {
|
||||
'feat-geocoding-title': 'بحث مكاني محلي',
|
||||
'feat-geocoding-desc': 'دقة عالية جداً في البحث عن المعالم والأحياء في الأردن وسوريا.',
|
||||
|
||||
// Dashboard Sidebar
|
||||
// Sidebar (Dashboard)
|
||||
'side-dashboard': 'لوحة التحكم',
|
||||
'side-playground': 'ساحة الاختبار',
|
||||
'side-playground': 'المختبر',
|
||||
'side-analytics': 'التحليلات',
|
||||
'side-billing': 'الفواتير',
|
||||
'side-audit': 'تدقيق الأماكن',
|
||||
'side-docs': 'التوثيق',
|
||||
'side-upgrade': 'ترقية الخطة',
|
||||
'side-beta': 'وصول تجريبي',
|
||||
'side-free-msg': 'أنت حالياً على الخطة المجانية.',
|
||||
|
||||
// Dashboard General
|
||||
'welcome': 'مرحباً بك مجدداً',
|
||||
// Header
|
||||
'system-status': 'النظام يعمل بكفاءة',
|
||||
'quota-card-title': 'رصيد الاستهلاك',
|
||||
'requests-left': 'طلب متبقي',
|
||||
'used-label': 'مستهلك',
|
||||
'lang-toggle': 'English',
|
||||
'loading': 'جاري التحميل...',
|
||||
|
||||
// KPI Labels
|
||||
// Login
|
||||
'login-title': 'انطلاق للخرائط',
|
||||
'login-subtitle': 'منصة المطورين الأولى لخدمات الخرائط في الأردن وسوريا.',
|
||||
'login-google': 'المتابعة باستخدام جوجل',
|
||||
'login-footer': 'جاهز للمؤسسات · وصول آمن',
|
||||
|
||||
// Home / Dashboard
|
||||
'welcome': 'مرحباً بك مجدداً',
|
||||
'tagline': 'كل ما تحتاجه للبناء باستخدام واجهة خرائط الأردن المتطورة',
|
||||
'kpi-total-req': 'إجمالي الطلبات',
|
||||
'kpi-success-rate': 'نسبة النجاح',
|
||||
'kpi-active-keys': 'المفاتيح النشطة',
|
||||
'kpi-latency': 'متوسط سرعة الاستجابة',
|
||||
'kpi-latency': 'متوسط الاستجابة',
|
||||
'quota-card-title': 'الرصيد الشهري',
|
||||
'req-left': 'طلب متبقي',
|
||||
'live': 'مباشر',
|
||||
'chart-request-volume': 'نشاط الطلبات',
|
||||
'chart-subtitle': 'حركة البيانات المباشرة عبر جميع الواجهات',
|
||||
'full-analytics': 'التحليلات الكاملة',
|
||||
'quick-start': 'البداية السريعة',
|
||||
'quick-start-msg': 'ابدأ الدمج باستخدام مكتبتنا البرمجية في ثوانٍ.',
|
||||
'install-npm': '# التثبيت عبر npm',
|
||||
'view-api-ref': 'عرض مرجع الـ API',
|
||||
'try-playground': 'تجربة المختبر',
|
||||
|
||||
// Keys Table
|
||||
'keys-title': 'مفاتيح الـ API الخاصة بك',
|
||||
'keys-subtitle': 'إدارة مفاتيح الوصول لتطبيقاتك',
|
||||
'keys-create': 'إنشاء مفتاح جديد',
|
||||
'keys-fetching': 'جاري جلب مفاتيحك الآمنة...',
|
||||
'table-name': 'الاسم',
|
||||
'table-key': 'مفتاح الـ API',
|
||||
'table-status': 'الحالة',
|
||||
'table-restrictions': 'القيود',
|
||||
'table-actions': 'الإجراءات',
|
||||
'table-loc-name': 'اسم الموقع',
|
||||
'table-match': 'المطابقة التلقائية',
|
||||
'table-spatial': 'السياق المكاني',
|
||||
'table-date': 'التاريخ',
|
||||
'table-amount': 'المبلغ',
|
||||
'table-provider': 'المزود',
|
||||
|
||||
// Playground
|
||||
'playground-subtitle': 'اختبر مفاتيحك وعاين خرائط الـ Vector في الوقت الفعلي',
|
||||
'pg-config': 'الإعدادات',
|
||||
'pg-active-key': 'مفتاح الـ API النشط',
|
||||
'pg-style': 'تنسيق الخريطة',
|
||||
'pg-search-placeholder': 'ابحث في عمان، الأردن...',
|
||||
|
||||
// Analytics
|
||||
'chart-request-volume': 'حجم الطلبات الكلية',
|
||||
'chart-latency-breakdown': 'تحليل سرعة الاستجابة (مللي ثانية)',
|
||||
'chart-success-ratio': 'نسبة نجاح الطلبات',
|
||||
'chart-days': ['الأحد', 'الاثنين', 'الثلاثاء', 'الأربعاء', 'الخميس', 'الجمعة', 'السبت'],
|
||||
'analytics-subtitle': 'رؤى عميقة حول أداء واجهات البرمجة الخاصة بك',
|
||||
'chart-success-ratio': 'نسبة النجاح',
|
||||
'chart-latency-breakdown': 'تفاصيل وقت الاستجابة',
|
||||
|
||||
// Audit
|
||||
'audit-subtitle': 'مراجعة واعتماد المواقع المقترحة من المستخدمين',
|
||||
'audit-empty': 'لا توجد مقترحات مواقع معلقة حالياً.',
|
||||
|
||||
// Billing
|
||||
'bill-current-plan': 'خطتك الحالية',
|
||||
'bill-renewal': 'تاريخ التجديد',
|
||||
'bill-usage-snapshot': 'نظرة على الاستهلاك',
|
||||
'bill-used': 'مستهلك',
|
||||
'bill-limit': 'الحد الأقصى',
|
||||
'bill-invoices': 'سجل الفواتير',
|
||||
'bill-status-active': 'نشطة',
|
||||
'billing-subtitle': 'إدارة خطتك، وسائل الدفع، وسجل الفواتير.',
|
||||
'current-plan': 'الخطة الحالية',
|
||||
'billing-reset-msg': 'يتم إعادة تعيين رصيدك في اليوم الأول من كل شهر.',
|
||||
'active': 'نشط',
|
||||
'plan-sandbox': 'تجريبية',
|
||||
'plan-starter-title': 'مبتدئ',
|
||||
'plan-pro-title': 'احترافي',
|
||||
'plan-ent-title': 'مؤسسات',
|
||||
'popular': 'الأكثر قيمة',
|
||||
'mo': 'شهر',
|
||||
'req-mo': 'طلب / شهر',
|
||||
'req-min': 'طلب / دقيقة',
|
||||
'feature-tiles': 'خرائط Vector القياسية',
|
||||
'feature-no-routing': 'بدون خدمة المسارات',
|
||||
'feature-3d': 'بيانات المباني ثلاثية الأبعاد',
|
||||
'feature-3d-ext': 'مباني ثلاثية الأبعاد (Extrusion)',
|
||||
'feature-basic-geo': 'تكويد جغرافي أساسي',
|
||||
'feature-routing': 'واجهة مسارات متطورة',
|
||||
'feature-priority': 'دعم فني ذو أولوية',
|
||||
'feature-custom': 'طبقات بيانات مخصصة',
|
||||
'feature-sla': 'ضمانات مستوى الخدمة',
|
||||
'feature-dedicated': 'مهندس دعم مخصص',
|
||||
'plan-current': 'الخطة الحالية',
|
||||
'plan-upgrade-starter': 'ترقية إلى "مبتدئ"',
|
||||
'pay-card': 'الدفع بالبطاقة',
|
||||
'contact-sales': 'تواصل مع المبيعات',
|
||||
'bill-transaction-history': 'سجل العمليات',
|
||||
|
||||
// Place Audit
|
||||
'audit-title': 'تدقيق الأماكن والمساهمات',
|
||||
'audit-desc': 'مراجعة واعتماد المواقع المصافة من قبل المجتمع.',
|
||||
'audit-no-data': 'لا توجد مساهمات معلقة حالياً.',
|
||||
// Modals
|
||||
'modal-key-title': 'إنشاء مفتاح API',
|
||||
'modal-key-subtitle': 'إعداد نقطة وصول جديدة لتطبيقك.',
|
||||
'modal-key-label': 'اسم المفتاح',
|
||||
'cancel': 'إلغاء',
|
||||
'guides-title': 'الأدلة برمجية'
|
||||
}
|
||||
},
|
||||
|
||||
init: () => {
|
||||
console.log(`🌍 i18n Initializing: ${i18n.currentLang}`);
|
||||
i18n.apply(i18n.currentLang);
|
||||
},
|
||||
|
||||
toggle: () => {
|
||||
const nextLang = i18n.currentLang === 'en' ? 'ar' : 'en';
|
||||
i18n.currentLang = nextLang;
|
||||
localStorage.setItem('intaleq_lang', nextLang);
|
||||
i18n.apply(nextLang);
|
||||
i18n.currentLang = i18n.currentLang === 'en' ? 'ar' : 'en';
|
||||
localStorage.setItem('intaleq_lang', i18n.currentLang);
|
||||
location.reload();
|
||||
},
|
||||
|
||||
apply: (lang) => {
|
||||
@@ -154,7 +290,7 @@ const i18n = {
|
||||
document.documentElement.dir = rtl ? 'rtl' : 'ltr';
|
||||
document.documentElement.lang = lang;
|
||||
|
||||
// Apply font classes
|
||||
// Apply font families
|
||||
if (rtl) {
|
||||
document.body.style.fontFamily = "'Cairo', sans-serif";
|
||||
document.body.classList.add('rtl-mode');
|
||||
@@ -163,7 +299,7 @@ const i18n = {
|
||||
document.body.classList.remove('rtl-mode');
|
||||
}
|
||||
|
||||
// Mirror Sidebar if in dashboard
|
||||
// Sidebar mirroring logic
|
||||
const sidebar = document.getElementById('main-sidebar');
|
||||
if (sidebar) {
|
||||
if (rtl) {
|
||||
@@ -175,7 +311,7 @@ const i18n = {
|
||||
}
|
||||
}
|
||||
|
||||
// Translate elements with data-i18n attribute
|
||||
// Apply text content
|
||||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
const translation = i18n.translations[lang][key];
|
||||
@@ -188,10 +324,17 @@ const i18n = {
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger icon re-render if lucide is present
|
||||
// Apply placeholders
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n-placeholder');
|
||||
const translation = i18n.translations[lang][key];
|
||||
if (translation) el.placeholder = translation;
|
||||
});
|
||||
|
||||
// Trigger icon re-render
|
||||
if (window.lucide) lucide.createIcons();
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize on load
|
||||
// Auto-init
|
||||
document.addEventListener('DOMContentLoaded', i18n.init);
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
# Rate limiting zones
|
||||
limit_req_zone $binary_remote_addr zone=tile_limit:10m rate=100r/s;
|
||||
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=30r/s;
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# Gzip compression for better performance
|
||||
gzip on;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
@@ -10,17 +18,26 @@ server {
|
||||
|
||||
# Proxy API requests to the api service in docker
|
||||
location /api/ {
|
||||
limit_req zone=api_limit burst=20 nodelay;
|
||||
proxy_pass http://api:3200/api/;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
|
||||
# Proxy Martin Vector Tiles (if accessed via dashboard directly)
|
||||
location /tiles/ {
|
||||
limit_req zone=tile_limit burst=50 nodelay;
|
||||
proxy_pass http://martin:3000/;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# Cache tiles for performance
|
||||
add_header Cache-Control "public, max-age=3600";
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'dart:convert';
|
||||
|
||||
class IntaleqMapController {
|
||||
final MapLibreMapController mapController;
|
||||
final String apiKey;
|
||||
|
||||
IntaleqMapController({
|
||||
required this.mapController,
|
||||
required this.apiKey,
|
||||
});
|
||||
|
||||
/// Search for places using Intaleq Geocoding API
|
||||
Future<List<dynamic>> search(String query) async {
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
'https://map-saas.intaleq.com/v1/geocoding/search?q=$query&key=$apiKey'),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return json.decode(response.body);
|
||||
}
|
||||
throw Exception('Failed to search');
|
||||
}
|
||||
|
||||
/// Get route using Intaleq Routing API
|
||||
Future<Map<String, dynamic>> getRoute(
|
||||
LatLng start,
|
||||
LatLng end, {
|
||||
String profile = 'car',
|
||||
}) async {
|
||||
final response = await http.get(
|
||||
Uri.parse(
|
||||
'https://map-saas.intaleq.com/v1/routing/route?start=${start.longitude},${start.latitude}&end=${end.longitude},${end.latitude}&profile=$profile&key=$apiKey',
|
||||
),
|
||||
);
|
||||
if (response.statusCode == 200) {
|
||||
return json.decode(response.body);
|
||||
}
|
||||
throw Exception('Failed to fetch route');
|
||||
}
|
||||
}
|
||||
|
||||
class IntaleqStyles {
|
||||
static String obsidian(String apiKey) =>
|
||||
'https://maps.intaleq.com/styles/obsidian/style.json?key=$apiKey';
|
||||
static String light(String apiKey) =>
|
||||
'https://maps.intaleq.com/styles/light/style.json?key=$apiKey';
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
name: intaleq_maps
|
||||
description: Premium Flutter SDK for Intaleq Map Platform (Jordan & Syria).
|
||||
version: 1.0.0
|
||||
homepage: https://intaleq.com
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
flutter: ">=3.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
maplibre_gl: ^0.19.0 # Use standard MapLibre plugin
|
||||
http: ^1.1.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
lints: ^2.1.0
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@intaleq/maps-gl",
|
||||
"version": "1.0.0",
|
||||
"description": "Premium JavaScript SDK for Intaleq Map Platform",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsup src/index.ts --format cjs,esm --dts",
|
||||
"dev": "tsup src/index.ts --format cjs,esm --watch --dts",
|
||||
"lint": "eslint src/**"
|
||||
},
|
||||
"keywords": [
|
||||
"maps",
|
||||
"intaleq",
|
||||
"jordan",
|
||||
"syria",
|
||||
"vector-tiles",
|
||||
"maplibre"
|
||||
],
|
||||
"author": "Intaleq team",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"maplibre-gl": "^5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"maplibre-gl": "^5.1.1",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import maplibregl from 'maplibre-gl';
|
||||
|
||||
export interface IntaleqMapOptions extends Omit<maplibregl.MapOptions, 'container'> {
|
||||
container: string | HTMLElement;
|
||||
apiKey: string;
|
||||
styleType?: 'obsidian' | 'light' | 'hybrid';
|
||||
}
|
||||
|
||||
/**
|
||||
* Intaleq Maps JS SDK
|
||||
* Optimized for Jordan & Syria mapping services.
|
||||
*/
|
||||
export class IntaleqMap extends maplibregl.Map {
|
||||
private readonly intaleqApiKey: string;
|
||||
|
||||
constructor(options: IntaleqMapOptions) {
|
||||
const { apiKey, styleType = 'obsidian', ...mapOptions } = options;
|
||||
|
||||
// Construct the Intaleq style URL
|
||||
// In production, this points to our tile server with the API key
|
||||
const styleUrl = `https://maps.intaleq.com/styles/${styleType}/style.json?key=${apiKey}`;
|
||||
|
||||
super({
|
||||
...mapOptions,
|
||||
style: styleUrl,
|
||||
hash: mapOptions.hash ?? true,
|
||||
center: mapOptions.center ?? [35.9239, 31.9522], // Default to Amman
|
||||
zoom: mapOptions.zoom ?? 12,
|
||||
transformRequest: (url: string) => {
|
||||
// Automatically append API key to all tile and asset requests
|
||||
if (url.includes('intaleq.com')) {
|
||||
const separator = url.includes('?') ? '&' : '?';
|
||||
return {
|
||||
url: `${url}${separator}key=${apiKey}`
|
||||
};
|
||||
}
|
||||
return { url };
|
||||
}
|
||||
});
|
||||
|
||||
this.intaleqApiKey = apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to search for places using Intaleq Geocoding API
|
||||
*/
|
||||
async search(query: string) {
|
||||
const response = await fetch(`https://map-saas.intaleq.com/v1/geocoding/search?q=${encodeURIComponent(query)}&key=${this.intaleqApiKey}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get routes using Intaleq Routing API
|
||||
*/
|
||||
async getRoute(start: [number, number], end: [number, number], profile: 'car' | 'bike' | 'foot' = 'car') {
|
||||
const response = await fetch(`https://map-saas.intaleq.com/v1/routing/route?start=${start.join(',')}&end=${end.join(',')}&profile=${profile}&key=${this.intaleqApiKey}`);
|
||||
return response.json();
|
||||
}
|
||||
}
|
||||
|
||||
export { maplibregl };
|
||||
Reference in New Issue
Block a user