|
|
@@ -0,0 +1,248 @@
|
|
|
+import { useState } from "react";
|
|
|
+import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
|
|
+import { Switch } from "@/components/ui/switch";
|
|
|
+import { Badge } from "@/components/ui/badge";
|
|
|
+import { Button } from "@/components/ui/button";
|
|
|
+import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
|
+import { Shield, ShieldCheck, ShieldAlert, Info, Loader2 } from "lucide-react";
|
|
|
+import { API_URL } from "@/lib/config";
|
|
|
+import { useToast } from "@/hooks/use-toast";
|
|
|
+
|
|
|
+interface DataAccessPermissions {
|
|
|
+ allow_customer_access: boolean;
|
|
|
+ allow_order_access: boolean;
|
|
|
+ allow_product_access: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+interface DataAccessSettingsProps {
|
|
|
+ storeId: string;
|
|
|
+ storeName: string;
|
|
|
+ currentPermissions: DataAccessPermissions;
|
|
|
+ onPermissionsUpdated?: (newPermissions: DataAccessPermissions) => void;
|
|
|
+}
|
|
|
+
|
|
|
+export function DataAccessSettings({
|
|
|
+ storeId,
|
|
|
+ storeName,
|
|
|
+ currentPermissions,
|
|
|
+ onPermissionsUpdated
|
|
|
+}: DataAccessSettingsProps) {
|
|
|
+ const [permissions, setPermissions] = useState<DataAccessPermissions>(currentPermissions);
|
|
|
+ const [saving, setSaving] = useState(false);
|
|
|
+ const [hasChanges, setHasChanges] = useState(false);
|
|
|
+ const { toast } = useToast();
|
|
|
+
|
|
|
+ const handleToggle = (key: keyof DataAccessPermissions) => {
|
|
|
+ setPermissions(prev => ({
|
|
|
+ ...prev,
|
|
|
+ [key]: !prev[key]
|
|
|
+ }));
|
|
|
+ setHasChanges(true);
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleSave = async () => {
|
|
|
+ try {
|
|
|
+ setSaving(true);
|
|
|
+ const sessionData = localStorage.getItem('session_data');
|
|
|
+ if (!sessionData) {
|
|
|
+ throw new Error('No session data found');
|
|
|
+ }
|
|
|
+
|
|
|
+ const session = JSON.parse(sessionData);
|
|
|
+ const response = await fetch(`${API_URL}/api/stores/${storeId}/permissions`, {
|
|
|
+ method: 'PUT',
|
|
|
+ headers: {
|
|
|
+ 'Authorization': `Bearer ${session.session.access_token}`,
|
|
|
+ 'Content-Type': 'application/json'
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ data_access_permissions: permissions
|
|
|
+ })
|
|
|
+ });
|
|
|
+
|
|
|
+ const data = await response.json();
|
|
|
+
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(data.error || 'Failed to update permissions');
|
|
|
+ }
|
|
|
+
|
|
|
+ toast({
|
|
|
+ title: "Permissions Updated",
|
|
|
+ description: "Data access permissions have been successfully updated.",
|
|
|
+ });
|
|
|
+
|
|
|
+ setHasChanges(false);
|
|
|
+ if (onPermissionsUpdated) {
|
|
|
+ onPermissionsUpdated(permissions);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error updating permissions:', error);
|
|
|
+ toast({
|
|
|
+ title: "Update Failed",
|
|
|
+ description: error instanceof Error ? error.message : "Failed to update permissions. Please try again.",
|
|
|
+ variant: "destructive",
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ setSaving(false);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const handleReset = () => {
|
|
|
+ setPermissions(currentPermissions);
|
|
|
+ setHasChanges(false);
|
|
|
+ };
|
|
|
+
|
|
|
+ const getSecurityLevel = () => {
|
|
|
+ const enabledCount = Object.values(permissions).filter(Boolean).length;
|
|
|
+ if (enabledCount === 0) return { level: "high", icon: ShieldCheck, color: "text-green-500", label: "High Privacy" };
|
|
|
+ if (enabledCount <= 1) return { level: "medium", icon: Shield, color: "text-yellow-500", label: "Medium Privacy" };
|
|
|
+ return { level: "low", icon: ShieldAlert, color: "text-orange-500", label: "Full Access" };
|
|
|
+ };
|
|
|
+
|
|
|
+ const security = getSecurityLevel();
|
|
|
+ const SecurityIcon = security.icon;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Card className="bg-slate-800 border-slate-700">
|
|
|
+ <CardHeader>
|
|
|
+ <div className="flex items-center justify-between">
|
|
|
+ <div>
|
|
|
+ <CardTitle className="text-white flex items-center gap-2">
|
|
|
+ <SecurityIcon className={`w-5 h-5 ${security.color}`} />
|
|
|
+ Data Access Permissions
|
|
|
+ </CardTitle>
|
|
|
+ <CardDescription className="text-slate-400">
|
|
|
+ Control which data types can be accessed via API for {storeName}
|
|
|
+ </CardDescription>
|
|
|
+ </div>
|
|
|
+ <Badge className={`${security.color.replace('text-', 'bg-')} text-white`}>
|
|
|
+ {security.label}
|
|
|
+ </Badge>
|
|
|
+ </div>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-6">
|
|
|
+ {/* GDPR Notice */}
|
|
|
+ <Alert className="bg-blue-500/10 border-blue-500/50">
|
|
|
+ <Info className="h-4 w-4 text-blue-400" />
|
|
|
+ <AlertDescription className="text-blue-300 text-sm">
|
|
|
+ <strong>GDPR Compliance:</strong> Customer and order data are NOT stored in our database.
|
|
|
+ They are fetched in real-time from your webshop when accessed via API.
|
|
|
+ Disabling access prevents API calls from retrieving this data.
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+
|
|
|
+ {/* Permission Toggles */}
|
|
|
+ <div className="space-y-4">
|
|
|
+ {/* Products Access */}
|
|
|
+ <div className="flex items-start justify-between p-4 bg-slate-700/50 rounded-lg">
|
|
|
+ <div className="flex-1">
|
|
|
+ <div className="flex items-center gap-2 mb-1">
|
|
|
+ <h4 className="text-white font-medium">Product Data Access</h4>
|
|
|
+ <Badge variant="outline" className="text-xs border-blue-500 text-blue-400">
|
|
|
+ Public Data
|
|
|
+ </Badge>
|
|
|
+ </div>
|
|
|
+ <p className="text-sm text-slate-400">
|
|
|
+ Allow API access to product information (names, prices, descriptions, stock levels)
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ <Switch
|
|
|
+ checked={permissions.allow_product_access}
|
|
|
+ onCheckedChange={() => handleToggle('allow_product_access')}
|
|
|
+ disabled={saving}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Customers Access */}
|
|
|
+ <div className="flex items-start justify-between p-4 bg-slate-700/50 rounded-lg">
|
|
|
+ <div className="flex-1">
|
|
|
+ <div className="flex items-center gap-2 mb-1">
|
|
|
+ <h4 className="text-white font-medium">Customer Data Access</h4>
|
|
|
+ <Badge variant="outline" className="text-xs border-orange-500 text-orange-400">
|
|
|
+ Personal Data
|
|
|
+ </Badge>
|
|
|
+ </div>
|
|
|
+ <p className="text-sm text-slate-400">
|
|
|
+ Allow API access to customer information (names, emails, addresses, purchase history)
|
|
|
+ </p>
|
|
|
+ {!permissions.allow_customer_access && (
|
|
|
+ <p className="text-xs text-yellow-400 mt-2">
|
|
|
+ ⚠️ API requests for customer data will be denied
|
|
|
+ </p>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <Switch
|
|
|
+ checked={permissions.allow_customer_access}
|
|
|
+ onCheckedChange={() => handleToggle('allow_customer_access')}
|
|
|
+ disabled={saving}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Orders Access */}
|
|
|
+ <div className="flex items-start justify-between p-4 bg-slate-700/50 rounded-lg">
|
|
|
+ <div className="flex-1">
|
|
|
+ <div className="flex items-center gap-2 mb-1">
|
|
|
+ <h4 className="text-white font-medium">Order Data Access</h4>
|
|
|
+ <Badge variant="outline" className="text-xs border-orange-500 text-orange-400">
|
|
|
+ Personal Data
|
|
|
+ </Badge>
|
|
|
+ </div>
|
|
|
+ <p className="text-sm text-slate-400">
|
|
|
+ Allow API access to order information (order details, amounts, customer info, shipping addresses)
|
|
|
+ </p>
|
|
|
+ {!permissions.allow_order_access && (
|
|
|
+ <p className="text-xs text-yellow-400 mt-2">
|
|
|
+ ⚠️ API requests for order data will be denied
|
|
|
+ </p>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <Switch
|
|
|
+ checked={permissions.allow_order_access}
|
|
|
+ onCheckedChange={() => handleToggle('allow_order_access')}
|
|
|
+ disabled={saving}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* Action Buttons */}
|
|
|
+ {hasChanges && (
|
|
|
+ <div className="flex items-center justify-end gap-3 pt-4 border-t border-slate-700">
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ onClick={handleReset}
|
|
|
+ disabled={saving}
|
|
|
+ className="text-slate-400 border-slate-600 hover:bg-slate-700"
|
|
|
+ >
|
|
|
+ Cancel
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ onClick={handleSave}
|
|
|
+ disabled={saving}
|
|
|
+ className="bg-cyan-500 hover:bg-cyan-600 text-white"
|
|
|
+ >
|
|
|
+ {saving ? (
|
|
|
+ <>
|
|
|
+ <Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
|
|
+ Saving...
|
|
|
+ </>
|
|
|
+ ) : (
|
|
|
+ 'Save Changes'
|
|
|
+ )}
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* Warning for disabling all access */}
|
|
|
+ {!permissions.allow_customer_access && !permissions.allow_order_access && (
|
|
|
+ <Alert className="bg-yellow-500/10 border-yellow-500/50">
|
|
|
+ <ShieldAlert className="h-4 w-4 text-yellow-400" />
|
|
|
+ <AlertDescription className="text-yellow-300 text-sm">
|
|
|
+ <strong>High Privacy Mode:</strong> Customer and order data access is disabled.
|
|
|
+ Only product data can be accessed via API. This maximizes privacy but limits functionality.
|
|
|
+ </AlertDescription>
|
|
|
+ </Alert>
|
|
|
+ )}
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ );
|
|
|
+}
|