Skip to content

Commit 641dc37

Browse files
Add Shopify API secrets
Added SHOPIFY_API_KEY and SHOPIFY_API_SECRET for Shopify OAuth functionality.
1 parent 7a42a43 commit 641dc37

File tree

11 files changed

+1112
-1
lines changed

11 files changed

+1112
-1
lines changed

src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import TrackingPage from '@/pages/TrackingPage';
2424
import ShortLinkPage from '@/pages/ShortLinkPage';
2525
import TrackingJsRoute from '@/pages/TrackingJsRoute';
2626
import { ShopifyCallbackPage } from '@/pages/ShopifyCallbackPage';
27+
import { ShopifyCallbackSupabasePage } from '@/pages/ShopifyCallbackSupabasePage';
28+
import { ShopifyTestPage } from '@/pages/ShopifyTestPage';
2729
import AdminPage from '@/pages/AdminPage';
2830
import AffiliateOnboardingPage from '@/pages/AffiliateOnboardingPage';
2931
import CommissionInfoPage from '@/pages/CommissionInfoPage';
@@ -67,6 +69,8 @@ function App() {
6769
<Route path="/app" element={<Index />} />
6870
<Route path="/dashboard" element={<Dashboard />} />
6971
<Route path="/auth/shopify/callback" element={<ShopifyCallbackPage />} />
72+
<Route path="/shopify/callback" element={<ShopifyCallbackSupabasePage />} />
73+
<Route path="/shopify/test" element={<ShopifyTestPage />} />
7074
<Route path="/admin" element={<AdminPage />} />
7175
<Route path="/campaign/:campaignId" element={<AdvancedStatsPage />} />
7276
<Route path="/affiliate/:affiliateId" element={<AffiliatePage />} />
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import React from 'react';
2+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3+
import { Button } from '@/components/ui/button';
4+
import { Badge } from '@/components/ui/badge';
5+
import { ShoppingBag, Trash2, ExternalLink, CheckCircle, AlertCircle } from 'lucide-react';
6+
import { ShopifyIntegration } from '@/hooks/useShopifySupabase';
7+
8+
interface ShopifyIntegrationCardProps {
9+
integration: ShopifyIntegration;
10+
onRemove: (id: string) => void;
11+
}
12+
13+
export const ShopifyIntegrationCard: React.FC<ShopifyIntegrationCardProps> = ({
14+
integration,
15+
onRemove
16+
}) => {
17+
const handleRemove = () => {
18+
if (window.confirm('Êtes-vous sûr de vouloir supprimer cette intégration Shopify ?')) {
19+
onRemove(integration.id);
20+
}
21+
};
22+
23+
const openShopAdmin = () => {
24+
window.open(`https://${integration.shop_domain}/admin`, '_blank');
25+
};
26+
27+
return (
28+
<Card className="w-full">
29+
<CardHeader className="pb-3">
30+
<div className="flex items-center justify-between">
31+
<div className="flex items-center space-x-3">
32+
<div className="p-2 bg-green-100 rounded-lg">
33+
<ShoppingBag className="h-5 w-5 text-green-600" />
34+
</div>
35+
<div>
36+
<CardTitle className="text-lg">{integration.shop_info.name}</CardTitle>
37+
<p className="text-sm text-muted-foreground">{integration.shop_domain}</p>
38+
</div>
39+
</div>
40+
<div className="flex items-center space-x-2">
41+
{integration.active ? (
42+
<Badge variant="default" className="bg-green-100 text-green-700">
43+
<CheckCircle className="h-3 w-3 mr-1" />
44+
Actif
45+
</Badge>
46+
) : (
47+
<Badge variant="secondary" className="bg-red-100 text-red-700">
48+
<AlertCircle className="h-3 w-3 mr-1" />
49+
Inactif
50+
</Badge>
51+
)}
52+
</div>
53+
</div>
54+
</CardHeader>
55+
56+
<CardContent className="space-y-4">
57+
<div className="grid grid-cols-2 gap-4 text-sm">
58+
<div>
59+
<span className="font-medium">Email:</span>
60+
<p className="text-muted-foreground">{integration.shop_info.email}</p>
61+
</div>
62+
<div>
63+
<span className="font-medium">Devise:</span>
64+
<p className="text-muted-foreground">{integration.shop_info.currency}</p>
65+
</div>
66+
<div>
67+
<span className="font-medium">Plan:</span>
68+
<p className="text-muted-foreground">{integration.shop_info.plan_name}</p>
69+
</div>
70+
<div>
71+
<span className="font-medium">Domaine:</span>
72+
<p className="text-muted-foreground">{integration.shop_info.domain}</p>
73+
</div>
74+
</div>
75+
76+
<div className="space-y-2">
77+
<h4 className="font-medium text-sm">Configuration:</h4>
78+
<div className="flex flex-wrap gap-2">
79+
<Badge variant={integration.settings.scriptsInstalled ? "default" : "secondary"}>
80+
Scripts: {integration.settings.scriptsInstalled ? 'Installés' : 'Non installés'}
81+
</Badge>
82+
<Badge variant={integration.settings.webhooksInstalled ? "default" : "secondary"}>
83+
Webhooks: {integration.settings.webhooksInstalled ? 'Actifs' : 'Inactifs'}
84+
</Badge>
85+
<Badge variant={integration.settings.autoInject ? "default" : "secondary"}>
86+
Auto-injection: {integration.settings.autoInject ? 'Activée' : 'Désactivée'}
87+
</Badge>
88+
</div>
89+
</div>
90+
91+
<div className="flex justify-between items-center pt-2">
92+
<div className="text-xs text-muted-foreground">
93+
Connecté le {new Date(integration.created_at).toLocaleDateString('fr-FR')}
94+
</div>
95+
<div className="flex space-x-2">
96+
<Button
97+
variant="outline"
98+
size="sm"
99+
onClick={openShopAdmin}
100+
className="flex items-center space-x-1"
101+
>
102+
<ExternalLink className="h-4 w-4" />
103+
<span>Admin</span>
104+
</Button>
105+
<Button
106+
variant="outline"
107+
size="sm"
108+
onClick={handleRemove}
109+
className="flex items-center space-x-1 text-red-600 hover:text-red-700"
110+
>
111+
<Trash2 className="h-4 w-4" />
112+
<span>Supprimer</span>
113+
</Button>
114+
</div>
115+
</div>
116+
</CardContent>
117+
</Card>
118+
);
119+
};
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import React, { useState } from 'react';
2+
import {
3+
Dialog,
4+
DialogContent,
5+
DialogDescription,
6+
DialogFooter,
7+
DialogHeader,
8+
DialogTitle,
9+
} from '@/components/ui/dialog';
10+
import { Button } from '@/components/ui/button';
11+
import { Input } from '@/components/ui/input';
12+
import { Label } from '@/components/ui/label';
13+
import { LoadingSpinner } from '@/components/shared/LoadingSpinner';
14+
import { ShoppingBag, ExternalLink } from 'lucide-react';
15+
16+
interface ShopifyIntegrationDialogProps {
17+
open: boolean;
18+
onOpenChange: (open: boolean) => void;
19+
onInstall: (shopName: string) => void;
20+
isLoading: boolean;
21+
}
22+
23+
export const ShopifyIntegrationDialog: React.FC<ShopifyIntegrationDialogProps> = ({
24+
open,
25+
onOpenChange,
26+
onInstall,
27+
isLoading
28+
}) => {
29+
const [shopName, setShopName] = useState('');
30+
31+
const handleSubmit = (e: React.FormEvent) => {
32+
e.preventDefault();
33+
if (shopName.trim()) {
34+
onInstall(shopName.trim());
35+
}
36+
};
37+
38+
const handleClose = () => {
39+
if (!isLoading) {
40+
setShopName('');
41+
onOpenChange(false);
42+
}
43+
};
44+
45+
return (
46+
<Dialog open={open} onOpenChange={handleClose}>
47+
<DialogContent className="sm:max-w-[500px]">
48+
<DialogHeader>
49+
<div className="flex items-center space-x-3 mb-2">
50+
<div className="p-2 bg-green-100 rounded-lg">
51+
<ShoppingBag className="h-6 w-6 text-green-600" />
52+
</div>
53+
<div>
54+
<DialogTitle>Connecter Shopify</DialogTitle>
55+
<DialogDescription>
56+
Connectez votre boutique Shopify pour activer le suivi d'affiliation
57+
</DialogDescription>
58+
</div>
59+
</div>
60+
</DialogHeader>
61+
62+
<form onSubmit={handleSubmit} className="space-y-4">
63+
<div className="space-y-2">
64+
<Label htmlFor="shopName">Nom de votre boutique Shopify</Label>
65+
<Input
66+
id="shopName"
67+
type="text"
68+
placeholder="ma-boutique"
69+
value={shopName}
70+
onChange={(e) => setShopName(e.target.value)}
71+
disabled={isLoading}
72+
className="w-full"
73+
/>
74+
<p className="text-xs text-muted-foreground">
75+
Entrez uniquement le nom de votre boutique (sans .myshopify.com)
76+
</p>
77+
</div>
78+
79+
<div className="bg-blue-50 p-4 rounded-lg space-y-2">
80+
<h4 className="font-medium text-blue-900">Que va-t-il se passer ?</h4>
81+
<ul className="text-sm text-blue-800 space-y-1">
82+
<li>• Vous serez redirigé vers Shopify pour autoriser l'application</li>
83+
<li>• RefSpring accédera à vos commandes et produits en lecture seule</li>
84+
<li>• Les scripts de suivi seront installés automatiquement</li>
85+
<li>• Vos commandes d'affiliation seront trackées en temps réel</li>
86+
</ul>
87+
</div>
88+
89+
<DialogFooter className="flex justify-between">
90+
<Button
91+
type="button"
92+
variant="outline"
93+
onClick={handleClose}
94+
disabled={isLoading}
95+
>
96+
Annuler
97+
</Button>
98+
<Button
99+
type="submit"
100+
disabled={!shopName.trim() || isLoading}
101+
className="flex items-center space-x-2"
102+
>
103+
{isLoading ? (
104+
<LoadingSpinner size="sm" />
105+
) : (
106+
<ExternalLink className="h-4 w-4" />
107+
)}
108+
<span>
109+
{isLoading ? 'Connexion...' : 'Connecter à Shopify'}
110+
</span>
111+
</Button>
112+
</DialogFooter>
113+
</form>
114+
</DialogContent>
115+
</Dialog>
116+
);
117+
};
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React, { useEffect, useState } from 'react';
2+
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3+
import { Button } from '@/components/ui/button';
4+
import { Badge } from '@/components/ui/badge';
5+
import { ShoppingBag, Plus } from 'lucide-react';
6+
import { useShopifySupabase } from '@/hooks/useShopifySupabase';
7+
import { ShopifyIntegrationCard } from './ShopifyIntegrationCard';
8+
import { ShopifyIntegrationDialog } from './ShopifyIntegrationDialog';
9+
10+
interface ShopifyTestComponentProps {
11+
campaignId: string;
12+
}
13+
14+
export const ShopifyTestComponent: React.FC<ShopifyTestComponentProps> = ({ campaignId }) => {
15+
const {
16+
integrations,
17+
isLoading,
18+
fetchIntegrations,
19+
initiateShopifyInstall,
20+
removeIntegration
21+
} = useShopifySupabase(campaignId);
22+
23+
const [showDialog, setShowDialog] = useState(false);
24+
25+
useEffect(() => {
26+
fetchIntegrations();
27+
}, [fetchIntegrations]);
28+
29+
const handleInstall = (shopName: string) => {
30+
// Stocker le campaignId pour le callback
31+
sessionStorage.setItem('shopify_campaign_id', campaignId);
32+
initiateShopifyInstall(shopName);
33+
};
34+
35+
return (
36+
<div className="space-y-6">
37+
<Card>
38+
<CardHeader>
39+
<div className="flex items-center justify-between">
40+
<div className="flex items-center space-x-3">
41+
<div className="p-2 bg-green-100 rounded-lg">
42+
<ShoppingBag className="h-6 w-6 text-green-600" />
43+
</div>
44+
<div>
45+
<CardTitle>Intégrations Shopify</CardTitle>
46+
<p className="text-sm text-muted-foreground">
47+
Connectez vos boutiques Shopify pour le suivi d'affiliation
48+
</p>
49+
</div>
50+
</div>
51+
<Button onClick={() => setShowDialog(true)} className="flex items-center space-x-2">
52+
<Plus className="h-4 w-4" />
53+
<span>Ajouter Shopify</span>
54+
</Button>
55+
</div>
56+
</CardHeader>
57+
58+
<CardContent>
59+
{integrations.length === 0 ? (
60+
<div className="text-center py-8">
61+
<div className="mx-auto w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mb-4">
62+
<ShoppingBag className="h-8 w-8 text-gray-400" />
63+
</div>
64+
<h3 className="font-medium text-gray-900 mb-2">Aucune boutique connectée</h3>
65+
<p className="text-sm text-gray-500 mb-4">
66+
Connectez votre première boutique Shopify pour commencer
67+
</p>
68+
<Button onClick={() => setShowDialog(true)} variant="outline">
69+
Connecter Shopify
70+
</Button>
71+
</div>
72+
) : (
73+
<div className="space-y-4">
74+
<div className="flex items-center justify-between">
75+
<Badge variant="secondary">
76+
{integrations.length} boutique{integrations.length > 1 ? 's' : ''} connectée{integrations.length > 1 ? 's' : ''}
77+
</Badge>
78+
</div>
79+
80+
<div className="grid gap-4">
81+
{integrations.map((integration) => (
82+
<ShopifyIntegrationCard
83+
key={integration.id}
84+
integration={integration}
85+
onRemove={removeIntegration}
86+
/>
87+
))}
88+
</div>
89+
</div>
90+
)}
91+
</CardContent>
92+
</Card>
93+
94+
<ShopifyIntegrationDialog
95+
open={showDialog}
96+
onOpenChange={setShowDialog}
97+
onInstall={handleInstall}
98+
isLoading={isLoading}
99+
/>
100+
</div>
101+
);
102+
};

0 commit comments

Comments
 (0)