1+ import { useState } from 'react' ;
2+ import { Dialog , DialogContent , DialogHeader , DialogTitle } from '@/components/ui/dialog' ;
3+ import { Button } from '@/components/ui/button' ;
4+ import { Input } from '@/components/ui/input' ;
5+ import { Label } from '@/components/ui/label' ;
6+ import { Textarea } from '@/components/ui/textarea' ;
7+ import { toast } from '@/hooks/use-toast' ;
8+ import { ExternalLink , Copy , CheckCircle } from 'lucide-react' ;
9+
10+ interface ShopifyPrivateAppDialogProps {
11+ open : boolean ;
12+ onOpenChange : ( open : boolean ) => void ;
13+ shopName : string ;
14+ onConnect : ( accessToken : string , shopDomain : string ) => void ;
15+ }
16+
17+ export const ShopifyPrivateAppDialog = ( {
18+ open,
19+ onOpenChange,
20+ shopName,
21+ onConnect
22+ } : ShopifyPrivateAppDialogProps ) => {
23+ const [ accessToken , setAccessToken ] = useState ( '' ) ;
24+ const [ loading , setLoading ] = useState ( false ) ;
25+
26+ const shopDomain = shopName . includes ( '.myshopify.com' ) ? shopName : `${ shopName } .myshopify.com` ;
27+ const shopifyUrl = `https://${ shopDomain } /admin/settings/apps` ;
28+
29+ const instructions = `1. Allez dans Paramètres → Applications et canaux de vente
30+ 2. Cliquez sur "Développer des applications"
31+ 3. Cliquez sur "Autoriser le développement d'applications personnalisées" (si nécessaire)
32+ 4. Cliquez sur "Créer une application"
33+ 5. Nommez l'app "RefSpring Integration"
34+ 6. Dans Configuration → Admin API access, accordez ces permissions :
35+ - Orders: read_orders, write_orders
36+ - Customers: read_customers, write_customers
37+ - Products: read_products
38+ - Script Tags: write_script_tags
39+ 7. Cliquez sur "Sauvegarder"
40+ 8. Dans "Identifiants d'API", copiez le "Admin API access token"
41+ 9. Collez ce token ci-dessous` ;
42+
43+ const handleConnect = async ( ) => {
44+ if ( ! accessToken . trim ( ) ) {
45+ toast ( {
46+ title : "Token requis" ,
47+ description : "Veuillez entrer votre Admin API access token" ,
48+ variant : "destructive"
49+ } ) ;
50+ return ;
51+ }
52+
53+ setLoading ( true ) ;
54+ try {
55+ // Valider le token en faisant un appel test à l'API Shopify
56+ const response = await fetch ( `https://${ shopDomain } /admin/api/2023-10/shop.json` , {
57+ headers : {
58+ 'X-Shopify-Access-Token' : accessToken ,
59+ 'Content-Type' : 'application/json'
60+ }
61+ } ) ;
62+
63+ if ( ! response . ok ) {
64+ throw new Error ( 'Token invalide ou boutique inaccessible' ) ;
65+ }
66+
67+ const data = await response . json ( ) ;
68+
69+ toast ( {
70+ title : "Connexion réussie !" ,
71+ description : `Connecté à ${ data . shop . name } ` ,
72+ } ) ;
73+
74+ onConnect ( accessToken , shopDomain ) ;
75+ onOpenChange ( false ) ;
76+
77+ } catch ( error ) {
78+ console . error ( 'Erreur validation token:' , error ) ;
79+ toast ( {
80+ title : "Erreur de connexion" ,
81+ description : "Vérifiez votre token et réessayez" ,
82+ variant : "destructive"
83+ } ) ;
84+ } finally {
85+ setLoading ( false ) ;
86+ }
87+ } ;
88+
89+ const copyInstructions = ( ) => {
90+ navigator . clipboard . writeText ( instructions ) ;
91+ toast ( {
92+ title : "Instructions copiées" ,
93+ description : "Les instructions ont été copiées dans le presse-papiers" ,
94+ } ) ;
95+ } ;
96+
97+ return (
98+ < Dialog open = { open } onOpenChange = { onOpenChange } >
99+ < DialogContent className = "max-w-2xl max-h-[80vh] overflow-y-auto" >
100+ < DialogHeader >
101+ < DialogTitle > Connecter votre boutique Shopify</ DialogTitle >
102+ </ DialogHeader >
103+
104+ < div className = "space-y-6" >
105+ < div className = "p-4 bg-muted rounded-lg" >
106+ < div className = "flex items-center justify-between mb-3" >
107+ < h3 className = "font-semibold" > Instructions étape par étape :</ h3 >
108+ < Button
109+ variant = "outline"
110+ size = "sm"
111+ onClick = { copyInstructions }
112+ className = "gap-2"
113+ >
114+ < Copy className = "h-4 w-4" />
115+ Copier
116+ </ Button >
117+ </ div >
118+ < Textarea
119+ value = { instructions }
120+ readOnly
121+ className = "min-h-[200px] text-sm bg-background"
122+ />
123+ </ div >
124+
125+ < div className = "flex justify-center" >
126+ < Button
127+ onClick = { ( ) => window . open ( shopifyUrl , '_blank' ) }
128+ className = "gap-2"
129+ >
130+ < ExternalLink className = "h-4 w-4" />
131+ Ouvrir votre admin Shopify
132+ </ Button >
133+ </ div >
134+
135+ < div className = "space-y-3" >
136+ < Label htmlFor = "access-token" > Admin API Access Token</ Label >
137+ < Input
138+ id = "access-token"
139+ type = "password"
140+ placeholder = "shpat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
141+ value = { accessToken }
142+ onChange = { ( e ) => setAccessToken ( e . target . value ) }
143+ className = "font-mono"
144+ />
145+ < p className = "text-sm text-muted-foreground" >
146+ Le token commence généralement par "shpat_"
147+ </ p >
148+ </ div >
149+
150+ < div className = "flex gap-3 justify-end" >
151+ < Button variant = "outline" onClick = { ( ) => onOpenChange ( false ) } >
152+ Annuler
153+ </ Button >
154+ < Button
155+ onClick = { handleConnect }
156+ disabled = { loading || ! accessToken . trim ( ) }
157+ className = "gap-2"
158+ >
159+ { loading ? (
160+ < >
161+ < div className = "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
162+ Vérification...
163+ </ >
164+ ) : (
165+ < >
166+ < CheckCircle className = "h-4 w-4" />
167+ Connecter
168+ </ >
169+ ) }
170+ </ Button >
171+ </ div >
172+ </ div >
173+ </ DialogContent >
174+ </ Dialog >
175+ ) ;
176+ } ;
0 commit comments