mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-11 17:26:45 +02:00
3 files changed
+90
-4
No files matched your search
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in new issue
Block a user