commit
c7dfbdd660
8 changed files with 86 additions and 49 deletions
|
|
@ -451,8 +451,12 @@ tabloRouter.post("/join", async (c) => {
|
|||
|
||||
await supabase.from("tablo_invites").delete().eq("id", invite_id);
|
||||
|
||||
const channel = streamServerClient.channel("messaging", tablo_id);
|
||||
await channel.addMembers([joiner.id]);
|
||||
try {
|
||||
const channel = streamServerClient.channel("messaging", tablo_id);
|
||||
await channel.addMembers([joiner.id]);
|
||||
} catch (error) {
|
||||
console.error("error adding member to channel", error);
|
||||
}
|
||||
|
||||
return c.json({ message: "Tablo joined successfully" });
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/jpg+xml" href="/public/icon.jpg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/src/ui-library/theme/index.css" />
|
||||
<title>XTablo</title>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,10 @@ export function EventTypeCard({
|
|||
return publicUrl;
|
||||
};
|
||||
return (
|
||||
<Card key={eventType.id} className={eventType.isActive ? "opacity-100" : "opacity-60"}>
|
||||
<Card
|
||||
key={eventType.id}
|
||||
className={`${eventType.isActive ? "opacity-100" : "opacity-60"} transition-shadow hover:shadow-lg`}
|
||||
>
|
||||
<CardHeader className="min-h-[80px]">
|
||||
<CardTitle className="text-lg">{eventType.name}</CardTitle>
|
||||
<CardAction>
|
||||
|
|
|
|||
|
|
@ -15,12 +15,11 @@ import {
|
|||
import { useTablosList } from "@ui/hooks/tablos";
|
||||
import { useGenerateWebcalToken } from "@ui/hooks/webcal";
|
||||
import { toast } from "@ui/lib/toast";
|
||||
import { CopyIcon } from "@ui/ui-library/icons";
|
||||
import { useState } from "react";
|
||||
import { Button } from "./ui/button";
|
||||
import { Input } from "./ui/input";
|
||||
import { Label } from "./ui/label";
|
||||
|
||||
import { CopyIcon } from "lucide-react";
|
||||
interface WebcalModalProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { Button } from "@ui/components/ui/button";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@ui/components/ui/card";
|
||||
import { useJoinTablo } from "@ui/hooks/invite";
|
||||
import { toast } from "@ui/lib/toast";
|
||||
import { useUser } from "@ui/providers/UserStoreProvider";
|
||||
import { CheckCircle2Icon, XCircleIcon } from "lucide-react";
|
||||
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
|
||||
|
||||
export const JoinPage = () => {
|
||||
|
|
@ -12,39 +15,35 @@ export const JoinPage = () => {
|
|||
const [searchParams] = useSearchParams();
|
||||
const token = searchParams.get("token");
|
||||
|
||||
if (!tablo_name || !token) {
|
||||
toast.add(
|
||||
{
|
||||
title: "Invitation invalide",
|
||||
description: "Veuillez vérifier le lien d'invitation",
|
||||
type: "error",
|
||||
},
|
||||
{
|
||||
timeout: 2000,
|
||||
}
|
||||
);
|
||||
navigate("/");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50">
|
||||
<div className="min-h-screen flex items-center justify-center bg-background p-4">
|
||||
<div className="max-w-md w-full">
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900">
|
||||
<h1 className="text-3xl font-bold text-foreground">
|
||||
Rejoindre le tablo "{tablo_name}"
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-8 rounded-lg shadow-lg">
|
||||
<div className="text-center mb-6">
|
||||
<h2 className="text-2xl font-semibold text-gray-900 mb-2">{tablo_name}</h2>
|
||||
<p className="text-gray-600">Vous avez été invité(e) à rejoindre ce tablo</p>
|
||||
</div>
|
||||
<Card className="transition-shadow hover:shadow-lg">
|
||||
<CardHeader className="text-center">
|
||||
<CardTitle className="text-2xl">{tablo_name}</CardTitle>
|
||||
<CardDescription>Vous avez été invité(e) à rejoindre ce tablo</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<button
|
||||
<CardContent className="space-y-4">
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!user || !token) return;
|
||||
if (!user || !token) {
|
||||
toast.add(
|
||||
{
|
||||
title: "Erreur",
|
||||
description: "Vous n'avez pas les permissions requises",
|
||||
type: "error",
|
||||
},
|
||||
{ timeout: 2000 }
|
||||
);
|
||||
return;
|
||||
}
|
||||
joinTablo(
|
||||
{ token },
|
||||
{
|
||||
|
|
@ -52,24 +51,31 @@ export const JoinPage = () => {
|
|||
navigate("/");
|
||||
},
|
||||
onError: (error) => {
|
||||
alert(error.message);
|
||||
toast.add(
|
||||
{
|
||||
title: "Erreur",
|
||||
description: error.message,
|
||||
type: "error",
|
||||
},
|
||||
{ timeout: 2000 }
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}}
|
||||
className="w-full bg-blue-600 text-white py-3 px-4 rounded-lg hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full"
|
||||
size="lg"
|
||||
>
|
||||
<CheckCircle2Icon className="w-5 h-5 mr-2" />
|
||||
Accepter l'invitation
|
||||
</button>
|
||||
</Button>
|
||||
|
||||
<button
|
||||
onClick={() => navigate("/")}
|
||||
className="w-full bg-gray-200 text-gray-800 py-3 px-4 rounded-lg hover:bg-gray-300"
|
||||
>
|
||||
<Button onClick={() => navigate("/")} variant="outline" className="w-full" size="lg">
|
||||
<XCircleIcon className="w-5 h-5 mr-2" />
|
||||
Refuser
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ export function LoginPage() {
|
|||
// 3D Parallax effect
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const [transform, setTransform] = useState("");
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!cardRef.current) return;
|
||||
|
|
@ -46,10 +47,12 @@ export function LoginPage() {
|
|||
setTransform(
|
||||
`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.01, 1.01, 1.01)`
|
||||
);
|
||||
setIsHovered(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setTransform("");
|
||||
setIsHovered(false);
|
||||
};
|
||||
|
||||
// Theme
|
||||
|
|
@ -92,8 +95,7 @@ export function LoginPage() {
|
|||
<div
|
||||
ref={cardRef}
|
||||
className={twMerge(
|
||||
"w-full max-w-lg rounded-2xl",
|
||||
"shadow-2xl shadow-black/20 dark:shadow-black/40",
|
||||
"w-full max-w-lg rounded-2xl relative",
|
||||
"transition-transform duration-200 ease-out will-change-transform"
|
||||
)}
|
||||
style={{ transform }}
|
||||
|
|
@ -101,7 +103,17 @@ export function LoginPage() {
|
|||
onMouseLeave={handleMouseLeave}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="relative w-full h-full p-8 bg-card/80 backdrop-blur-md rounded-2xl border border-border z-10">
|
||||
{/* Glow effect behind the card */}
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/10 via-primary/5 to-secondary/10 blur-xl -z-10"></div>
|
||||
|
||||
<div
|
||||
className={twMerge(
|
||||
"relative w-full h-full p-8 bg-card/80 backdrop-blur-md rounded-2xl border border-border z-10 transition-shadow duration-200",
|
||||
isHovered
|
||||
? "shadow-[0_20px_50px_rgba(0,0,0,0.3)] dark:shadow-[0_20px_50px_rgba(0,0,0,0.6)]"
|
||||
: "shadow-2xl shadow-black/20 dark:shadow-black/40"
|
||||
)}
|
||||
>
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<a
|
||||
href="https://www.xtablo.com"
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ export function SignUpPage() {
|
|||
// 3D Parallax effect
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
const [transform, setTransform] = useState("");
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (!cardRef.current) return;
|
||||
|
|
@ -50,10 +51,12 @@ export function SignUpPage() {
|
|||
setTransform(
|
||||
`perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.01, 1.01, 1.01)`
|
||||
);
|
||||
setIsHovered(true);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setTransform("");
|
||||
setIsHovered(false);
|
||||
};
|
||||
|
||||
// Theme
|
||||
|
|
@ -138,8 +141,7 @@ export function SignUpPage() {
|
|||
<div
|
||||
ref={cardRef}
|
||||
className={twMerge(
|
||||
"w-full max-w-xl rounded-2xl",
|
||||
"shadow-2xl shadow-black/20 dark:shadow-black/40",
|
||||
"w-full max-w-xl rounded-2xl relative",
|
||||
"transition-transform duration-200 ease-out will-change-transform"
|
||||
)}
|
||||
style={{ transform }}
|
||||
|
|
@ -147,7 +149,17 @@ export function SignUpPage() {
|
|||
onMouseLeave={handleMouseLeave}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<div className="relative w-full h-full p-6 bg-card/80 backdrop-blur-md rounded-2xl border border-border z-10">
|
||||
{/* Glow effect behind the card */}
|
||||
<div className="absolute inset-0 rounded-2xl bg-gradient-to-br from-primary/10 via-primary/5 to-secondary/10 blur-xl -z-10"></div>
|
||||
|
||||
<div
|
||||
className={twMerge(
|
||||
"relative w-full h-full p-6 bg-card/80 backdrop-blur-md rounded-2xl border border-border z-10 transition-shadow duration-200",
|
||||
isHovered
|
||||
? "shadow-[0_20px_50px_rgba(0,0,0,0.3)] dark:shadow-[0_20px_50px_rgba(0,0,0,0.6)]"
|
||||
: "shadow-2xl shadow-black/20 dark:shadow-black/40"
|
||||
)}
|
||||
>
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<a
|
||||
href="https://www.xtablo.com"
|
||||
|
|
|
|||
|
|
@ -373,8 +373,10 @@ export const TabloPage = () => {
|
|||
|
||||
{/* Trash Icon - Only show for admins */}
|
||||
{isAdmin && (
|
||||
<button
|
||||
className="absolute top-2 right-2 p-1.5 bg-destructive hover:bg-destructive/90 text-destructive-foreground rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10"
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="absolute top-2 right-2 p-1.5 rounded-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 z-10 text-red-600 hover:text-red-700 hover:bg-red-50 border-red-200"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDeleteTablo(tablo.id);
|
||||
|
|
@ -382,7 +384,7 @@ export const TabloPage = () => {
|
|||
title="Supprimer le tablo"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* Read-only indicator for non-admins */}
|
||||
|
|
|
|||
Loading…
Reference in a new issue