Skip to content

Commit de5b441

Browse files
support avec email client, fix cleaned worker avant la fin, fix unlink mastodon
1 parent 6c97fe2 commit de5b441

File tree

18 files changed

+511
-74
lines changed

18 files changed

+511
-74
lines changed

messages/en.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,5 +306,31 @@
306306
"hqxNewsletter": "Receive updates from HelloQuitX",
307307
"oepNewsletter": "Be informed about On est prêt initiatives"
308308
}
309+
},
310+
"support": {
311+
"modal": {
312+
"title": "A problem occured ?",
313+
"description": "Send us a message and we'll get back to you as soon as possible.",
314+
"error": {
315+
"send": "Error sending message",
316+
"unknown": "An unexpected error occurred"
317+
},
318+
"form": {
319+
"email": {
320+
"label": "Your email address",
321+
"placeholder": "[email protected]"
322+
},
323+
"subject": {
324+
"label": "Subject",
325+
"placeholder": "Your subject"
326+
},
327+
"message": {
328+
"label": "Message",
329+
"placeholder": "Your message"
330+
},
331+
"submit": "Send",
332+
"submitting": "Sending..."
333+
}
334+
}
309335
}
310336
}

messages/fr.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,5 +294,31 @@
294294
"hqxNewsletter": "Recevoir les updates de HelloQuitteX",
295295
"oepNewsletter": "Être informé.e des initiatives de On est prêt"
296296
}
297+
},
298+
"support": {
299+
"modal": {
300+
"title": "Un problème ?",
301+
"description": "Envoyez-nous un message et nous vous répondrons dans les plus brefs délais.",
302+
"error": {
303+
"send": "Erreur lors de l'envoi du message",
304+
"unknown": "Une erreur inattendue s'est produite"
305+
},
306+
"form": {
307+
"email": {
308+
"label": "Votre adresse email",
309+
"placeholder": "[email protected]"
310+
},
311+
"subject": {
312+
"label": "Sujet",
313+
"placeholder": "Votre sujet"
314+
},
315+
"message": {
316+
"label": "Message",
317+
"placeholder": "Votre message"
318+
},
319+
"submit": "Envoyer",
320+
"submitting": "Envoi en cours..."
321+
}
322+
}
297323
}
298324
}

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"next": "15.0.3",
2929
"next-auth": "^5.0.0-beta.25",
3030
"next-intl": "^3.26.3",
31+
"nodemailer": "^6.9.16",
3132
"react": "^18.2.0",
3233
"react-dom": "^18.2.0",
3334
"react-icons": "^5.4.0",

src/app/[locale]/auth/signin/page.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ import LoginSea from "@/app/_components/LoginSea"
1313
import Footer from "@/app/_components/Footer";
1414
import { useTranslations } from 'next-intl'
1515

16-
// import logoHQXFR from '../../../../../public/logoxHQX/HQX-rose-FR.svg';
17-
// import logoHQXEN from '../../../../../public/logoxHQX/HQX-pink-UK.svg';
18-
1916
export default function SignIn() {
2017
const { data: session, status } = useSession()
2118
const router = useRouter()

src/app/[locale]/dashboard/page.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export default function DashboardPage() {
8282
const hasTwitter = session?.user?.twitter_id;
8383
const hasOnboarded = session?.user?.has_onboarded;
8484

85+
const connectedServicesCount = [hasMastodon, hasBluesky, hasTwitter].filter(Boolean).length;
86+
8587
// Ajoutez cette vérification
8688
useEffect(() => {
8789
if (status === "unauthenticated" ) {
@@ -286,7 +288,6 @@ export default function DashboardPage() {
286288
</div>
287289
)}
288290

289-
{!hasOnboarded && (
290291
<>
291292
<div className="flex justify-center relative z-10">
292293
<div className="w-full max-w-2xl flex gap-4">
@@ -302,7 +303,7 @@ export default function DashboardPage() {
302303
<span className="text-lg font-semibold">{t('addSocialNetwork')}</span>
303304
<Link className="w-6 h-6 opacity-90" />
304305
</motion.button>
305-
306+
{!hasOnboarded && (
306307
<motion.button
307308
whileHover={{ scale: 1.05 }}
308309
whileTap={{ scale: 0.95 }}
@@ -318,6 +319,7 @@ export default function DashboardPage() {
318319
<span className="text-lg font-semibold">{t('importButton')}</span>
319320
<Ship className="w-6 h-6 opacity-90" />
320321
</motion.button>
322+
)}
321323
</div>
322324
</div>
323325

@@ -370,7 +372,6 @@ export default function DashboardPage() {
370372
</>
371373
)}
372374
</>
373-
)}
374375
</div>
375376
</div>
376377
</div>

src/app/[locale]/upload/page.tsx

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import { validateTwitterData, extractTargetFiles } from '../../_components/Uploa
1414
import Image from 'next/image';
1515
import seaBackground from '../../../public/sea.svg'
1616
import { plex } from '../../fonts/plex';
17-
import logoHQX from '../../../../public/logoxHQX/HQX-rose-FR.svg'
1817
import { motion } from 'framer-motion';
18+
import { AlertCircle } from 'lucide-react';
1919
import boat1 from '../../../public/boats/boat-1.svg'
2020
import Footer from "@/app/_components/Footer";
2121
import LoadingIndicator from '@/app/_components/LoadingIndicator';
22-
22+
import SupportModal from '../../_components/SupportModale';
23+
import logoHQXFR from '../../../../public/logoxHQX/HQX-rose-FR.svg';
24+
import logoHQXEN from '../../../../public/logoxHQX/HQX-pink-UK.svg';
2325

2426
const UploadButton = dynamic(() => import('../../_components/UploadButton'), {
2527
loading: () => <div className="animate-pulse bg-gray-200 h-12 w-48 rounded-lg"></div>,
@@ -43,7 +45,11 @@ export default function UploadPage() {
4345
const [isUploading, setIsUploading] = useState(false);
4446
const [isLoading, setIsLoading] = useState(true);
4547
const [showHelpModal, setShowHelpModal] = useState(false);
48+
const [showSupportModal, setShowSupportModal] = useState(false);
4649
const t = useTranslations('upload');
50+
const tSupport = useTranslations('support');
51+
const locale = params.locale as string;
52+
const logoHQX = locale === 'fr' ? logoHQXFR : logoHQXEN;
4753

4854
useEffect(() => {
4955
if (status === "unauthenticated") {
@@ -141,11 +147,22 @@ export default function UploadPage() {
141147
};
142148

143149
const validateFileType = (file: File): boolean => {
144-
145150
console.log("File Type =", file.type)
146-
const validTypes = ['application/javascript', 'text/javascript', 'application/zip', 'application/x-javascript'];
151+
const validTypes = [
152+
'application/javascript',
153+
'text/javascript',
154+
'application/zip',
155+
'application/x-javascript',
156+
'text/ecmascript',
157+
'application/ecmascript',
158+
'application/x-ecmascript',
159+
'text/x-javascript',
160+
'text/jsx',
161+
'text/plain',
162+
'module'
163+
];
147164
return validTypes.includes(file.type);
148-
};
165+
};
149166

150167
const sanitizeContent = (content: Uint8Array): Uint8Array => {
151168
const text = new TextDecoder().decode(content);
@@ -404,8 +421,7 @@ export default function UploadPage() {
404421
}
405422

406423
return (
407-
<div className="min-h-screen bg-[#2a39a9] relative w-full max-w-[90rem] m-auto">
408-
<Header />
424+
<div className="min-h-screen bg-[#2a39a9] relative w-full max-w-[90rem] m-auto"> <Header />
409425
<div className="relative z-10 pt-12">
410426
<Image
411427
src={logoHQX}
@@ -416,6 +432,7 @@ export default function UploadPage() {
416432
priority
417433
/>
418434
</div>
435+
419436
<div className="flex justify-center items-center min-h-[60vh]">
420437
<motion.div
421438
initial={{ opacity: 0, y: 20 }}
@@ -443,20 +460,54 @@ export default function UploadPage() {
443460
</p>
444461

445462
{!isUploading && (
446-
<div className="mt-8">
463+
<div className="space-y-4">
447464
<UploadButton onFilesSelected={handleFilesSelected} onError={handleUploadError} />
465+
<motion.button
466+
whileHover={{ scale: 1.05 }}
467+
whileTap={{ scale: 0.95 }}
468+
onClick={() => setShowSupportModal(true)}
469+
className="w-full flex items-center justify-center gap-2 py-3 px-6 bg-white/10 hover:bg-white/20 backdrop-blur-lg rounded-xl text-white"
470+
>
471+
<AlertCircle className="w-5 h-5" />
472+
<span>{tSupport('modal.title')}</span>
473+
</motion.button>
448474
</div>
449475
)}
450476

451477
{isUploading && (
452478
<div className="flex items-center justify-center space-x-2">
453479
<div className="animate-spin rounded-full h-5 w-5 border-t-2 border-b-2 border-white"></div>
454-
<span>{t('loading')}</span>
480+
<p className="text-white">{t('uploading')}</p>
455481
</div>
456482
)}
457483
</div>
458484
</div>
485+
459486
</motion.div>
487+
488+
{/* Modal de Support */}
489+
<SupportModal
490+
isOpen={showSupportModal}
491+
onClose={() => setShowSupportModal(false)}
492+
/>
493+
494+
{/* </motion.div> */}
495+
496+
{/* <motion.button
497+
whileHover={{ scale: 1.05 }}
498+
whileTap={{ scale: 0.95 }}
499+
onClick={() => setShowSupportModal(true)}
500+
className="fixed bottom-4 right-4 bg-white/10 hover:bg-white/20 backdrop-blur-lg rounded-full p-3 text-white shadow-lg flex items-center gap-2"
501+
>
502+
<AlertCircle className="w-5 h-5" />
503+
<span>{tSupport('button')}</span>
504+
</motion.button> */}
505+
506+
{/* Modal de Support */}
507+
<SupportModal
508+
isOpen={showSupportModal}
509+
onClose={() => setShowSupportModal(false)}
510+
/>
460511
</div>
461512

462513
{/* Modals */}

src/app/_components/DashboardLoginButtons.tsx

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,36 @@ export default function DashboardLoginButtons({
7171
const t = useTranslations('dashboardLoginButtons')
7272

7373
const handleSignIn = async (provider: string) => {
74-
onLoadingChange(true)
75-
await signIn(provider, {
76-
callbackUrl: '/dashboard?linking=true'
77-
})
74+
try {
75+
onLoadingChange(true)
76+
const result = await signIn(provider, {
77+
redirect: false,
78+
callbackUrl: '/dashboard?linking=true'
79+
})
80+
81+
if (result?.error) {
82+
// Redirect to error page with appropriate error code
83+
if (result.error.includes("temporairement indisponible")) {
84+
window.location.href = `/auth/error?error=RateLimit`;
85+
} else if (result.error.includes("Configuration")) {
86+
window.location.href = `/auth/error?error=Configuration`;
87+
} else if (result.error.includes("OAuthSignin")) {
88+
window.location.href = `/auth/error?error=OAuthSignin`;
89+
} else if (result.error.includes("OAuthCallback")) {
90+
window.location.href = `/auth/error?error=OAuthCallback`;
91+
} else if (result.error.includes("AccessDenied")) {
92+
window.location.href = `/auth/error?error=AccessDenied`;
93+
} else {
94+
window.location.href = `/auth/error?error=Default&message=${encodeURIComponent(result.error)}`;
95+
}
96+
} else if (result?.ok && result.url) {
97+
window.location.href = result.url;
98+
}
99+
} catch (err: any) {
100+
window.location.href = `/auth/error?error=Default&message=${encodeURIComponent(err.message || "Une erreur inattendue s'est produite")}`;
101+
} finally {
102+
onLoadingChange(false)
103+
}
78104
}
79105

80106
if (!hasUploadedArchive) {
@@ -218,7 +244,7 @@ export default function DashboardLoginButtons({
218244
variants={itemVariants}
219245
whileHover={{ scale: 1.01, y: -2 }}
220246
whileTap={{ scale: 0.99 }}
221-
onClick={() => handleSignIn("mastodon-piaille")}
247+
onClick={() => handleSignIn("piaille")}
222248
className="w-full flex items-center justify-center gap-3 px-4 py-4
223249
bg-gradient-to-br from-purple-500/80 to-purple-600/80 rounded-xl
224250
hover:from-purple-500 hover:to-purple-600

src/app/_components/Footer.tsx

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
'use client';
22

33
import { useTranslations } from 'next-intl';
4-
import { memo } from 'react';
4+
import { memo, useState } from 'react';
55
import { Github, Mail } from 'lucide-react';
6+
import SupportModal from './SupportModale';
67

78
const FooterLink = memo(({ href, children }: { href: string; children: React.ReactNode }) => (
89
<a
910
href={href}
10-
target="_blank"
11-
rel="noopener noreferrer"
1211
className="text-indigo-300 hover:text-pink-400 transition-colors duration-200"
1312
>
1413
{children}
@@ -20,6 +19,7 @@ FooterLink.displayName = 'FooterLink';
2019
const Footer = memo(() => {
2120
const t = useTranslations('footer');
2221
const year = new Date().getFullYear();
22+
const [isSupportModalOpen, setIsSupportModalOpen] = useState(false);
2323

2424
const hostedText = t.raw('hosted.text');
2525
const cnrsText = t.raw('hosted.cnrs');
@@ -58,12 +58,19 @@ const Footer = memo(() => {
5858
<FooterLink href="https://github.com/FannyCaulfield/helloquittex">
5959
<Github className="w-5 h-5" />
6060
</FooterLink>
61-
<FooterLink href="mailto:[email protected]">
61+
<button
62+
onClick={() => setIsSupportModalOpen(true)}
63+
className="text-indigo-300 hover:text-pink-400 transition-colors duration-200"
64+
>
6265
<Mail className="w-5 h-5" />
63-
</FooterLink>
66+
</button>
6467
</div>
6568
</div>
6669
</div>
70+
<SupportModal
71+
isOpen={isSupportModalOpen}
72+
onClose={() => setIsSupportModalOpen(false)}
73+
/>
6774
</footer>
6875
);
6976
});

0 commit comments

Comments
 (0)