Merge pull request #81 from R0m1k3/dev

Dev
This commit is contained in:
LogiFlow authored and GitHub committed 2025-10-28 13:49:09 +01:00
commit 8afc5a642d
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