Improve calendar view and add scheduling conflict warnings

Enhance the calendar list view to prioritize today's date and introduce visual indicators for optimal posting times and existing scheduled posts on the new post page.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda
Replit-Commit-Checkpoint-Type: intermediate_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/VJoXNjs
This commit is contained in:
michaelschal committed 2025-10-28 12:43:14 +00:00
1 parent 44ec6abe00
commit eec5e397fe
3 files changed
+90 -4

No files matched your search

+9 -2
View File
@@ -42,8 +42,15 @@ export default function CalendarListView({ scheduledPosts, onEditPost, onDeleteP
return acc;
}, {});
// Sort dates
const sortedDates = Object.keys(groupedPosts).sort();
// Sort dates - put today first, then chronological order
const today = format(new Date(), "yyyy-MM-dd");
const sortedDates = Object.keys(groupedPosts).sort((a, b) => {
// Today always comes first
if (a === today && b !== today) return -1;
if (b === today && a !== today) return 1;
// Otherwise chronological order
return a.localeCompare(b);
});
const toggleDate = (date: string) => {
const newExpanded = new Set(expandedDates);
+80 -2
View File
@@ -32,7 +32,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
import { Dialog, DialogContent } from "@/components/ui/dialog";
import { useToast } from "@/hooks/use-toast";
import { apiRequest, queryClient } from "@/lib/queryClient";
import type { SocialPage, Media } from "@shared/schema";
import type { SocialPage, Media, ScheduledPost } from "@shared/schema";
import { PreviewModal } from "@/components/preview-modal";
function SortableMediaItem({
@@ -127,6 +127,10 @@ export default function NewPost() {
queryKey: ['/api/media'],
});
const { data: scheduledPosts = [] } = useQuery<ScheduledPost[]>({
queryKey: ['/api/scheduled-posts'],
});
const uploadMutation = useMutation({
mutationFn: async (file: File) => {
const formData = new FormData();
@@ -707,7 +711,7 @@ export default function NewPost() {
<CardDescription>Programmez la publication (optionnel)</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="space-y-3">
<Label htmlFor="scheduledDate">Date et heure</Label>
<Input
id="scheduledDate"
@@ -719,6 +723,80 @@ export default function NewPost() {
<p className="text-xs text-muted-foreground">
Laissez vide pour publier immédiatement
</p>
{(() => {
// Get dates with scheduled posts
const datesWithPosts = scheduledPosts
.filter(post => post.scheduledAt)
.map(post => {
const date = new Date(post.scheduledAt!);
return date.toISOString().split('T')[0];
})
.filter((date, index, self) => self.indexOf(date) === index)
.sort();
if (datesWithPosts.length > 0) {
return (
<div className="p-3 bg-red-500/10 rounded-lg border border-red-500/20">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-red-500"></div>
<span className="text-xs font-semibold text-red-600 dark:text-red-400">
Dates avec publications programmées
</span>
</div>
<div className="flex flex-wrap gap-2">
{datesWithPosts.slice(0, 5).map((date) => {
const dateObj = new Date(date + 'T00:00:00');
const formatted = dateObj.toLocaleDateString('fr-FR', {
day: '2-digit',
month: 'short'
});
return (
<span
key={date}
className="text-xs px-2 py-1 bg-red-500/20 text-red-700 dark:text-red-300 rounded-md font-medium"
>
{formatted}
</span>
);
})}
{datesWithPosts.length > 5 && (
<span className="text-xs px-2 py-1 text-red-600 dark:text-red-400 font-medium">
+{datesWithPosts.length - 5} autre{datesWithPosts.length - 5 > 1 ? 's' : ''}
</span>
)}
</div>
<p className="text-[10px] text-red-600/80 dark:text-red-400/80 mt-2">
Ces dates ont déjà des publications planifiées
</p>
</div>
);
}
return null;
})()}
<div className="p-3 bg-green-500/10 rounded-lg border border-green-500/20">
<div className="flex items-center gap-2 mb-2">
<div className="w-2 h-2 rounded-full bg-green-500"></div>
<span className="text-xs font-semibold text-green-600 dark:text-green-400">
Heures optimales pour l'audience
</span>
</div>
<div className="flex flex-wrap gap-2">
<span className="text-xs px-2 py-1 bg-green-500/20 text-green-700 dark:text-green-300 rounded-md font-medium">
8h - 9h
</span>
<span className="text-xs px-2 py-1 bg-green-500/20 text-green-700 dark:text-green-300 rounded-md font-medium">
12h - 14h
</span>
<span className="text-xs px-2 py-1 bg-green-500/20 text-green-700 dark:text-green-300 rounded-md font-medium">
18h - 21h
</span>
</div>
<p className="text-[10px] text-green-600/80 dark:text-green-400/80 mt-2">
Ces créneaux maximisent la portée de vos publications
</p>
</div>
</div>
</CardContent>
</Card>
+1
View File
@@ -11,6 +11,7 @@ Preferred communication style: Simple, everyday language.
## Recent Changes
### October 28, 2025
- **Calendar Mobile UX Improvements**: Enhanced mobile calendar and publication planning interface with three visual improvements: (1) Mobile calendar list now displays today's date at the top, before chronological ordering, for quick access to current day's publications; (2) Added green visual indicators in new-post page showing optimal posting times (8h-9h, 12h-14h, 18h-21h) to maximize audience reach; (3) Added red visual indicators showing dates with existing scheduled posts to prevent scheduling conflicts. Both indicators appear dynamically below the datetime selector, displaying up to 5 dates with option to view more.
- **Facebook Video Story Publishing Fix**: Fixed critical bug preventing video story publication. Facebook Graph API requires a 3-phase upload process for video stories: (1) START phase - initialize upload session and get `video_id` + `upload_url`, (2) UPLOAD phase - POST video to rupload.facebook.com endpoint with `file_url` header and OAuth authorization, (3) FINISH phase - finalize and publish story with `video_id`. Previous implementation incorrectly attempted direct upload to `/video_stories` endpoint with `file_url` parameter, causing "#100 The parameter upload_phase is required" error. Fixed by implementing proper 3-phase workflow using remote URL upload method (file_url header), avoiding need for binary chunked uploads.
### October 17, 2025