@@ -27,6 +27,17 @@ export const action = async ({ request }: LoaderFunctionArgs) => {
2727 const decoded = jwt . decode ( idToken . toString ( ) ) as JwtPayload ;
2828
2929 if ( type === "insert" ) {
30+ const existingKey = await prisma . apiKey . findFirst ( {
31+ where : {
32+ userId : decoded . sub ?? "" ,
33+ organizationId : orgId . toString ( ) ,
34+ shop : decoded . dest ?? "" ,
35+ } ,
36+ } ) ;
37+ if ( existingKey ) {
38+ return existingKey ;
39+ }
40+
3041 const key = await prisma . apiKey . create ( {
3142 data : {
3243 userId : decoded . sub ?? "" ,
@@ -55,6 +66,7 @@ type Orgs = {
5566export default function App ( ) {
5667 const fetcher = useFetcher < typeof action > ( ) ;
5768 const [ orgs , setOrgs ] = useState < Orgs [ ] > ( [ ] ) ;
69+ const [ successfulLogin , setSuccessfulLogin ] = useState ( false ) ;
5870 const envs = useEnvs ( ) ;
5971
6072 useEffect ( ( ) => {
@@ -77,7 +89,7 @@ export default function App() {
7789
7890 useEffect ( ( ) => {
7991 if ( fetcher . data ?. key ) {
80- window . close ( ) ;
92+ setSuccessfulLogin ( true ) ;
8193 }
8294 } , [ fetcher . data ?. key ] ) ;
8395
@@ -93,21 +105,26 @@ export default function App() {
93105 "TR-Organization" : selectedOrg . toString ( ) ,
94106 } ,
95107 body : JSON . stringify ( { name : "Shopify-Access" , role : 2 } ) ,
96- } ) . then ( ( response ) => {
97- response . json ( ) . then ( ( data ) => {
98- let params = new URLSearchParams ( window . location . search ) ;
99-
100- fetcher . submit (
101- {
102- apiKey : data . api_key ,
103- orgId : selectedOrg ,
104- idToken : params . get ( "token" ) ,
105- type : "insert" ,
106- } ,
107- { method : "POST" } ,
108- ) ;
108+ } )
109+ . then ( ( response ) => {
110+ response . json ( ) . then ( ( data ) => {
111+ let params = new URLSearchParams ( window . location . search ) ;
112+
113+ fetcher . submit (
114+ {
115+ apiKey : data . api_key ,
116+ orgId : selectedOrg ,
117+ idToken : params . get ( "token" ) ,
118+ type : "insert" ,
119+ } ,
120+ { method : "POST" } ,
121+ ) ;
122+ } ) ;
123+ } )
124+ . catch ( ( error ) => {
125+ console . error ( "Error generating API key:" , error ) ;
126+ setSuccessfulLogin ( true ) ;
109127 } ) ;
110- } ) ;
111128 } ;
112129
113130 return (
@@ -120,28 +137,53 @@ export default function App() {
120137 />
121138 < span className = "text-2xl font-semibold" > Trieve</ span >
122139 </ div >
123- { orgs . length > 0 && (
124- < div className = "rounded-md border border-neutral-300 bg-white p-4 md:min-w-[500px]" >
125- < div className = "flex justify-between" >
126- < div className = "text-lg font-medium" > Select An Organization</ div >
127- </ div >
128- < div className = "flex flex-col py-2" >
129- { orgs ?. map ( ( org ) => (
130- < button
131- onClick = { ( ) => {
132- generateApiKey ( org . id ) ;
133- } }
134- className = "flex cursor-pointer items-center justify-between rounded-md border-b border-b-neutral-200 p-2 last:border-b-transparent hover:bg-neutral-100"
135- >
136- < div className = "flex w-full items-center justify-between" >
137- < div className = "text-sm font-medium" > { org . name } </ div >
138- < div className = "text-xs text-neutral-500" > { org . id } </ div >
140+ < div className = "rounded-md border border-neutral-300 bg-white p-4 md:min-w-[500px]" >
141+ { successfulLogin && (
142+ < >
143+ < div className = "flex justify-between" >
144+ < div className = "text-lg font-medium" > Login Successful 🎉</ div >
145+ </ div >
146+ < div className = "py-2 text-sm text-neutral-700" >
147+ You can now close this window and return to your Shopify store.
148+ </ div >
149+ < div className = "py-2 text-sm text-neutral-700" >
150+ If you don't see the app, please refresh the page.
151+ </ div >
152+ </ >
153+ ) }
154+ { ! successfulLogin &&
155+ ( orgs . length > 0 ? (
156+ < >
157+ < div className = "flex justify-between" >
158+ < div className = "text-lg font-medium" >
159+ Select An Organization
139160 </ div >
140- </ button >
141- ) ) }
142- </ div >
143- </ div >
144- ) }
161+ </ div >
162+ < div className = "flex flex-col py-2" >
163+ { orgs ?. map ( ( org ) => (
164+ < button
165+ key = { org . id }
166+ onClick = { ( ) => {
167+ generateApiKey ( org . id ) ;
168+ } }
169+ className = "flex cursor-pointer items-center justify-between rounded-md border-b border-b-neutral-200 p-2 last:border-b-transparent hover:bg-neutral-100"
170+ >
171+ < div className = "flex w-full items-center justify-between" >
172+ < div className = "text-sm font-medium" > { org . name } </ div >
173+ < div className = "text-xs text-neutral-500" > { org . id } </ div >
174+ </ div >
175+ </ button >
176+ ) ) }
177+ </ div >
178+ </ >
179+ ) : (
180+ < div className = "flex justify-center items-center w-full" >
181+ < div className = "flex h-10 w-10 animate-spin items-center justify-center rounded-full border-4 border-neutral-300 border-t-transparent" >
182+ < div className = "h-6 w-6 animate-spin rounded-full border-4 border-neutral-300 border-t-transparent" />
183+ </ div >
184+ </ div >
185+ ) ) }
186+ </ div >
145187 </ div >
146188 ) ;
147189}
0 commit comments