Enable users to edit and delete their managed social media pages

Adds PUT and DELETE API endpoints for social pages, along with UI elements for editing and deleting pages on the client-side.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/Ob3gzfQ
This commit is contained in:
michaelschal committed 2025-10-02 16:45:44 +00:00
1 parent 82b73620dd
commit cce7c511d0
3 files changed
+161 -15

No files matched your search

+1 -5
View File
@@ -15,13 +15,9 @@ localPort = 5000
externalPort = 80
[[ports]]
localPort = 5555
localPort = 35981
externalPort = 3002
[[ports]]
localPort = 40011
externalPort = 3003
[[ports]]
localPort = 40537
externalPort = 3000
+121 -10
View File
@@ -1,6 +1,6 @@
import { useState } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { Plus, Facebook, Instagram, Trash2, RefreshCw } from "lucide-react";
import { Plus, Facebook, Instagram, Trash2, RefreshCw, Edit } from "lucide-react";
import Sidebar from "@/components/sidebar";
import TopBar from "@/components/topbar";
import { Button } from "@/components/ui/button";
@@ -16,6 +16,7 @@ import type { SocialPage } from "@shared/schema";
export default function PagesManagement() {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [dialogOpen, setDialogOpen] = useState(false);
const [editingPage, setEditingPage] = useState<SocialPage | null>(null);
const { toast } = useToast();
const { data: pages = [], isLoading } = useQuery<SocialPage[]>({
@@ -69,6 +70,7 @@ export default function PagesManagement() {
</p>
</div>
<AddPageDialog open={dialogOpen} onOpenChange={setDialogOpen} />
<EditPageDialog page={editingPage} onOpenChange={(open) => !open && setEditingPage(null)} />
</div>
{isLoading ? (
@@ -125,15 +127,25 @@ export default function PagesManagement() {
<div className="text-sm text-muted-foreground">
ID: {page.pageId}
</div>
<Button
variant="ghost"
size="icon"
onClick={() => deleteMutation.mutate(page.id)}
disabled={deleteMutation.isPending}
data-testid={`button-delete-page-${page.id}`}
>
<Trash2 className="w-4 h-4 text-destructive" />
</Button>
<div className="flex gap-2">
<Button
variant="ghost"
size="icon"
onClick={() => setEditingPage(page)}
data-testid={`button-edit-page-${page.id}`}
>
<Edit className="w-4 h-4" />
</Button>
<Button
variant="ghost"
size="icon"
onClick={() => deleteMutation.mutate(page.id)}
disabled={deleteMutation.isPending}
data-testid={`button-delete-page-${page.id}`}
>
<Trash2 className="w-4 h-4 text-destructive" />
</Button>
</div>
</div>
</CardContent>
</Card>
@@ -293,3 +305,102 @@ function AddPageDialog({ open, onOpenChange }: { open: boolean; onOpenChange: (o
</Dialog>
);
}
function EditPageDialog({ page, onOpenChange }: { page: SocialPage | null; onOpenChange: (open: boolean) => void }) {
const [accessToken, setAccessToken] = useState('');
const { toast } = useToast();
const editMutation = useMutation({
mutationFn: (data: any) =>
apiRequest('PUT', `/api/pages/${page?.id}`, data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['/api/pages'] });
toast({
title: "Page modifiée",
description: "Le jeton d'accès a été mis à jour avec succès",
});
onOpenChange(false);
setAccessToken('');
},
onError: () => {
toast({
title: "Erreur",
description: "Impossible de modifier la page",
variant: "destructive",
});
},
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!page) return;
editMutation.mutate({
accessToken,
});
};
if (!page) return null;
return (
<Dialog open={!!page} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>Modifier le jeton d'accès</DialogTitle>
<DialogDescription>
Mettez à jour le jeton d'accès pour {page.pageName}
</DialogDescription>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="editAccessToken">Nouveau jeton d'accès</Label>
<Input
id="editAccessToken"
type="password"
value={accessToken}
onChange={(e) => setAccessToken(e.target.value)}
placeholder="EAAxxxxxxxxxxxxx"
required
data-testid="input-edit-access-token"
/>
<p className="text-xs text-muted-foreground">
Obtenez un nouveau jeton depuis{' '}
<a
href="https://developers.facebook.com/tools/explorer"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Facebook Graph API Explorer
</a>
</p>
</div>
<div className="flex justify-end gap-2">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
data-testid="button-cancel-edit"
>
Annuler
</Button>
<Button
type="submit"
disabled={editMutation.isPending}
data-testid="button-submit-edit"
>
{editMutation.isPending ? (
<>
<RefreshCw className="w-4 h-4 mr-2 animate-spin" />
Modification...
</>
) : (
'Modifier'
)}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
);
}
+39
View File
@@ -541,6 +541,45 @@ export async function registerRoutes(app: Express): Promise<Server> {
}
});
app.put("/api/pages/:id", requireAuth, async (req, res) => {
try {
const user = req.user as User;
const userId = user.id;
const pageId = req.params.id;
const existingPage = await storage.getSocialPage(pageId);
if (!existingPage || existingPage.userId !== userId) {
return res.status(404).json({ error: "Page non trouvée" });
}
const pageData = insertSocialPageSchema.partial().parse(req.body);
const updatedPage = await storage.updateSocialPage(pageId, pageData);
res.json(updatedPage);
} catch (error) {
console.error("Error updating page:", error);
res.status(500).json({ error: "Failed to update page" });
}
});
app.delete("/api/pages/:id", requireAuth, async (req, res) => {
try {
const user = req.user as User;
const userId = user.id;
const pageId = req.params.id;
const existingPage = await storage.getSocialPage(pageId);
if (!existingPage || existingPage.userId !== userId) {
return res.status(404).json({ error: "Page non trouvée" });
}
await storage.deleteSocialPage(pageId);
res.json({ success: true });
} catch (error) {
console.error("Error deleting page:", error);
res.status(500).json({ error: "Failed to delete page" });
}
});
// AI Generations
app.get("/api/ai/generations", requireAuth, async (req, res) => {
try {