diff --git a/apps/frontend/src/app/(app)/auth/page.tsx b/apps/frontend/src/app/(app)/auth/page.tsx
index d5e6847..d7bcfae 100644
--- a/apps/frontend/src/app/(app)/auth/page.tsx
+++ b/apps/frontend/src/app/(app)/auth/page.tsx
@@ -3,9 +3,8 @@ export const dynamic = 'force-dynamic';
 import { Register } from '@gitroom/frontend/components/auth/register';
 import { Metadata } from 'next';
 import { isGeneralServerSide } from '@gitroom/helpers/utils/is.general.server.side';
-import Link from 'next/link';
 import { getT } from '@gitroom/react/translation/get.translation.service.backend';
-import { LoginWithOidc } from '@gitroom/frontend/components/auth/login.with.oidc';
+import { Login } from '@gitroom/frontend/components/auth/login';
 export const metadata: Metadata = {
   title: `${isGeneralServerSide() ? 'Postiz' : 'Gitroom'} Register`,
   description: '',
@@ -19,13 +18,9 @@ export default async function Auth(params: {searchParams: Promise<{provider: str
     if (!canRegister && !(await params?.searchParams)?.provider) {
       return (
         <>
-          <LoginWithOidc />
+          <Login />
           <div className="text-center">
             {t('registration_is_disabled', 'Registration is disabled')}
-            <br />
-            <Link className="underline hover:font-bold" href="/auth/login">
-              {t('login_instead', 'Login instead')}
-            </Link>
           </div>
         </>
       );
diff --git a/apps/frontend/src/app/colors.scss b/apps/frontend/src/app/colors.scss
index e719580..7f4c49f 100644
--- a/apps/frontend/src/app/colors.scss
+++ b/apps/frontend/src/app/colors.scss
@@ -26,6 +26,16 @@
     --new-col-color: #2c2b2b;
     --new-menu-dots: #696868;
     --new-menu-hover: #fff;
+    --new-status-error: #f97066;
+    --new-status-error-surface: #3b1715;
+    --new-status-warning: #fdb022;
+    --new-status-warning-surface: #35270c;
+    --new-status-info: #53b1fd;
+    --new-status-info-surface: #102a43;
+    --new-status-success: #47cd89;
+    --new-status-success-surface: #123524;
+    --new-status-neutral: #d0d5dd;
+    --new-status-neutral-surface: #292d33;
     --menu-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.5);
     --popup-color: rgba(65, 64, 66, 0.3);
     --border-preview: transparent;
@@ -37,11 +47,11 @@
     --facebook-bg-comment: #333334;
     --instagram-bg: #0b1014;
     --tiktok-item-bg: #2a2a2a;
-    --tiktok-item-icon-bg: #FFF;
-    --youtube-bg: #0F0F0F;
-    --youtube-button: #F1F1F1;
+    --tiktok-item-icon-bg: #fff;
+    --youtube-bg: #0f0f0f;
+    --youtube-button: #f1f1f1;
     --youtube-action-color: #272727;
-    --youtube-svg-border: #A0A0A0;
+    --youtube-svg-border: #a0a0a0;
   }
   .light {
     --new-back-drop: #2d1b57;
@@ -70,6 +80,16 @@
     --new-col-color: #eff1f3;
     --new-menu-dots: #696868;
     --new-menu-hover: #000;
+    --new-status-error: #b42318;
+    --new-status-error-surface: #fef3f2;
+    --new-status-warning: #b54708;
+    --new-status-warning-surface: #fffaeb;
+    --new-status-info: #175cd3;
+    --new-status-info-surface: #eff8ff;
+    --new-status-success: #067647;
+    --new-status-success-surface: #ecfdf3;
+    --new-status-neutral: #475467;
+    --new-status-neutral-surface: #f2f4f7;
     --menu-shadow: -22px 83px 24px 0 rgba(55, 52, 75, 0),
       -14px 53px 22px 0 rgba(55, 52, 75, 0.01),
       -8px 30px 19px 0 rgba(55, 52, 75, 0.05),
@@ -87,12 +107,12 @@
     --facebook-bg: #fff;
     --facebook-bg-comment: #f6f6f6;
     --instagram-bg: #fff;
-    --tiktok-item-bg: #EEF1F0;
+    --tiktok-item-bg: #eef1f0;
     --tiktok-item-icon-bg: #454645;
-    --youtube-bg: #FFF;
+    --youtube-bg: #fff;
     --youtube-button: #000;
-    --youtube-action-color: #F1F1F1;
-    --youtube-svg-border: #1A1A1A;
+    --youtube-action-color: #f1f1f1;
+    --youtube-svg-border: #1a1a1a;
   }
 }
 
diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx
index 793fc92..c222a55 100644
--- a/apps/frontend/src/components/auth/login.tsx
+++ b/apps/frontend/src/components/auth/login.tsx
@@ -23,6 +23,7 @@ type Inputs = {
 };
 export function Login() {
   const t = useT();
+  const registrationDisabled = process.env.DISABLE_REGISTRATION === 'true';
   const [loading, setLoading] = useState(false);
   const [notActivated, setNotActivated] = useState(false);
   const { isGeneral, neynarClientId, billingEnabled, genericOauth } =
@@ -69,29 +70,35 @@ export function Login() {
               {t('sign_in', 'Sign In')}
             </h1>
           </div>
-          <div className="text-[14px] mt-[32px] mb-[12px]">
-            {t('continue_with', 'Continue With')}
-          </div>
-          <div className="flex flex-col">
-            {isGeneral && genericOauth ? (
-              <OauthProvider />
-            ) : !isGeneral ? (
-              <GithubProvider />
-            ) : (
-              <div className="gap-[8px] flex">
-                <GoogleProvider />
-                {!!neynarClientId && <FarcasterProvider />}
-                {billingEnabled && <WalletProvider />}
+          {!registrationDisabled && (
+            <>
+              <div className="text-[14px] mt-[32px] mb-[12px]">
+                {t('continue_with', 'Continue With')}
               </div>
-            )}
-            <div className="h-[20px] mb-[24px] mt-[24px] relative">
-              <div className="absolute w-full h-[1px] bg-fifth top-[50%] -translate-y-[50%]" />
-              <div
-                className={`absolute z-[1] justify-center items-center w-full start-0 -top-[4px] flex`}
-              >
-                <div className="px-[16px]">{t('or', 'or')}</div>
+              <div className="flex flex-col">
+                {isGeneral && genericOauth ? (
+                  <OauthProvider />
+                ) : !isGeneral ? (
+                  <GithubProvider />
+                ) : (
+                  <div className="gap-[8px] flex">
+                    <GoogleProvider />
+                    {!!neynarClientId && <FarcasterProvider />}
+                    {billingEnabled && <WalletProvider />}
+                  </div>
+                )}
+                <div className="h-[20px] mb-[24px] mt-[24px] relative">
+                  <div className="absolute w-full h-[1px] bg-fifth top-[50%] -translate-y-[50%]" />
+                  <div
+                    className={`absolute z-[1] justify-center items-center w-full start-0 -top-[4px] flex`}
+                  >
+                    <div className="px-[16px]">{t('or', 'or')}</div>
+                  </div>
+                </div>
               </div>
-            </div>
+            </>
+          )}
+          <div className="flex flex-col">
             <div className="flex flex-col gap-[12px]">
               <div className="text-textColor">
                 <Input
@@ -136,12 +143,15 @@ export function Login() {
                     {t('sign_in_1', 'Sign in')}
                   </Button>
                 </div>
-                <p className="mt-4 text-sm">
-                  {t('don_t_have_an_account', "Don't Have An Account?")}&nbsp;
-                  <Link href="/auth" className="underline cursor-pointer">
-                    {t('sign_up', 'Sign Up')}
-                  </Link>
-                </p>
+                {!registrationDisabled && (
+                  <p className="mt-4 text-sm">
+                    {t('don_t_have_an_account', "Don't Have An Account?")}
+                    &nbsp;
+                    <Link href="/auth" className="underline cursor-pointer">
+                      {t('sign_up', 'Sign Up')}
+                    </Link>
+                  </p>
+                )}
                 <p className="mt-4 text-sm">
                   <Link
                     href="/auth/forgot"
diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx
index fb5bea3..8eaf130 100644
--- a/apps/frontend/src/components/launches/calendar.context.tsx
+++ b/apps/frontend/src/components/launches/calendar.context.tsx
@@ -9,108 +9,74 @@ import {
   useContext,
   useEffect,
   useMemo,
+  useRef,
   useState,
 } from 'react';
-import dayjs from 'dayjs';
+import dayjs, { extend } from 'dayjs';
+import isoWeek from 'dayjs/plugin/isoWeek';
+import weekOfYear from 'dayjs/plugin/weekOfYear';
 import useSWR from 'swr';
 import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
-import { Post, Integration, Tags } from '@prisma/client';
 import { useSearchParams } from 'next/navigation';
-import isoWeek from 'dayjs/plugin/isoWeek';
-import weekOfYear from 'dayjs/plugin/weekOfYear';
-import { extend } from 'dayjs';
 import useCookie from 'react-use-cookie';
 import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone';
-import { timer } from '@gitroom/helpers/utils/timer';
-import { expandPostsList, expandPosts } from '@gitroom/helpers/utils/posts.list.minify';
+import {
+  buildCalendarListRequestUrl,
+  buildCalendarRequestUrl,
+  buildLaunchesSearchParams,
+  INTEGRATION_QUERY_PARAM,
+  normalizeIntegrationIds,
+  parseIntegrationIds,
+} from '@gitroom/frontend/components/launches/calendar-filter-query';
+import { useCalendarPosts } from '@gitroom/frontend/components/launches/helpers/use.calendar.posts';
+import { useCalendarListPosts } from '@gitroom/frontend/components/launches/helpers/use.calendar.list.posts';
+import type {
+  CalendarContextValue,
+  CalendarDisplay,
+  CalendarFilterUpdate,
+  CalendarFilters,
+  CalendarIntegration,
+  CalendarPost,
+  ListStateFilter,
+} from '@gitroom/frontend/components/launches/calendar.types';
+
 extend(isoWeek);
 extend(weekOfYear);
 
-export type ListStateFilter = 'all' | 'scheduled' | 'draft' | 'published';
+export type Integrations = CalendarIntegration;
+export type { ListStateFilter } from './calendar.types';
 
-export const CalendarContext = createContext({
+const defaultFilters: CalendarFilters = {
   startDate: newDayjs().startOf('isoWeek').format('YYYY-MM-DD'),
   endDate: newDayjs().endOf('isoWeek').format('YYYY-MM-DD'),
-  customer: null as string | null,
-  loading: true,
-  sets: [] as { name: string; id: string; content: string[] }[],
-  signature: undefined as any,
-  comments: [] as Array<{
-    date: string;
-    total: number;
-  }>,
-  integrations: [] as (Integrations & {
-    refreshNeeded?: boolean;
-  })[],
-  trendings: [] as string[],
-  posts: [] as Array<
-    Post & {
-      integration: Integration;
-      tags: {
-        tag: Tags;
-      }[];
-    }
-  >,
-  reloadCalendarView: () => {
-    /** empty **/
-  },
+  customer: null,
   display: 'week',
-  setFilters: (filters: {
-    startDate: string;
-    endDate: string;
-    display: 'week' | 'month' | 'day' | 'list';
-    customer: string | null;
-  }) => {
-    /** empty **/
-  },
-  changeDate: (id: string, date: dayjs.Dayjs) => {
-    /** empty **/
-  },
-  // List view specific
-  listPosts: [] as Array<
-    Post & {
-      integration: Integration;
-      tags: {
-        tag: Tags;
-      }[];
-    }
-  >,
+  integrationIds: [],
+};
+
+export const CalendarContext = createContext<CalendarContextValue>({
+  ...defaultFilters,
+  loading: true,
+  requestError: false,
+  sets: [],
+  signature: undefined,
+  comments: [],
+  integrations: [],
+  visibleIntegrations: [],
+  trendings: [],
+  posts: [],
+  reloadCalendarView: () => undefined,
+  setFilters: () => undefined,
+  changeDate: () => undefined,
+  listPosts: [],
   listPage: 0,
   listTotalPages: 0,
-  setListPage: (page: number) => {
-    /** empty **/
-  },
-  listState: 'all' as ListStateFilter,
-  setListState: (state: ListStateFilter) => {
-    /** empty **/
-  },
+  setListPage: () => undefined,
+  listState: 'all',
+  setListState: () => undefined,
 });
 
-export interface Integrations {
-  name: string;
-  id: string;
-  disabled?: boolean;
-  inBetweenSteps: boolean;
-  editor: 'none' | 'normal' | 'markdown' | 'html';
-  stripLinks?: boolean;
-  display: string;
-  identifier: string;
-  type: string;
-  picture: string;
-  changeProfilePicture: boolean;
-  additionalSettings: string;
-  changeNickName: boolean;
-  time: {
-    time: number;
-  }[];
-  customer?: {
-    name?: string;
-    id?: string;
-  };
-}
-
-// Helper function to get start and end dates based on display type
-function getDateRange(display: string, referenceDate?: string) {
+function getDateRange(display: CalendarDisplay, referenceDate?: string) {
   const date = referenceDate ? newDayjs(referenceDate) : newDayjs();
 
   switch (display) {
@@ -119,17 +85,13 @@ function getDateRange(display: string, referenceDate?: string) {
         startDate: date.format('YYYY-MM-DD'),
         endDate: date.format('YYYY-MM-DD'),
       };
-    case 'week':
-      return {
-        startDate: date.startOf('isoWeek').format('YYYY-MM-DD'),
-        endDate: date.endOf('isoWeek').format('YYYY-MM-DD'),
-      };
     case 'month':
       return {
         startDate: date.startOf('month').format('YYYY-MM-DD'),
         endDate: date.endOf('month').format('YYYY-MM-DD'),
       };
-    default:
+    case 'week':
+    case 'list':
       return {
         startDate: date.startOf('isoWeek').format('YYYY-MM-DD'),
         endDate: date.endOf('isoWeek').format('YYYY-MM-DD'),
@@ -137,120 +99,138 @@ function getDateRange(display: string, referenceDate?: string) {
   }
 }
 
+function parseDisplay(value: string | null, fallback: string): CalendarDisplay {
+  const display = value || fallback;
+  return display === 'day' ||
+    display === 'week' ||
+    display === 'month' ||
+    display === 'list'
+    ? display
+    : 'week';
+}
+
+function parseListState(value: string | null): ListStateFilter {
+  return value === 'scheduled' || value === 'draft' || value === 'published'
+    ? value
+    : 'all';
+}
+
 export const CalendarWeekProvider: FC<{
   children: ReactNode;
-  integrations: Integrations[];
-}> = ({ children, integrations }) => {
+  integrations: CalendarIntegration[];
+  integrationsReady: boolean;
+}> = ({ children, integrations, integrationsReady }) => {
   const fetch = useFetch();
-  const [internalData, setInternalData] = useState([] as any[]);
+  const [internalData, setInternalData] = useState<CalendarPost[]>([]);
   const [trendings] = useState<string[]>([]);
   const searchParams = useSearchParams();
   const [displaySaved, setDisplaySaved] = useCookie('calendar-display', 'week');
-  const display = searchParams.get('display') || displaySaved;
+  const initialDisplay = parseDisplay(
+    searchParams.get('display'),
+    displaySaved
+  );
+  const availableIntegrationIds = useMemo(
+    () => new Set(integrations.map((integration) => integration.id)),
+    [integrations]
+  );
 
-  // List view state
   const [listPage, setListPage] = useState(0);
-  const [listState, setListStateRaw] = useState<ListStateFilter>('all');
-  const setListState = useCallback((next: ListStateFilter) => {
-    setListStateRaw(next);
-    setListPage(0);
-  }, []);
-
-  // Initialize with current date range based on URL params or defaults
-  const initStartDate = searchParams.get('startDate');
-  const initEndDate = searchParams.get('endDate');
-  const initCustomer = searchParams.get('customer');
-
+  const [listState, setListStateRaw] = useState<ListStateFilter>(() =>
+    parseListState(searchParams.get('state'))
+  );
+  const listStateRef = useRef(listState);
   const initialRange =
-    initStartDate && initEndDate
-      ? { startDate: initStartDate, endDate: initEndDate }
-      : getDateRange(display);
-
-  const [filters, setFilters] = useState({
+    searchParams.get('startDate') && searchParams.get('endDate')
+      ? {
+          startDate: searchParams.get('startDate')!,
+          endDate: searchParams.get('endDate')!,
+        }
+      : getDateRange(initialDisplay);
+  const initialRawIntegrationIds = Array.from(
+    new Set(
+      (searchParams.get('integrationIds')?.split(',') || [])
+        .map((id) => id.trim())
+        .filter(Boolean)
+    )
+  ).sort();
+  const pendingUrlIntegrationIds = useRef<string[] | null>(
+    initialRawIntegrationIds
+  );
+  const [filters, setFilters] = useState<CalendarFilters>({
     startDate: initialRange.startDate,
     endDate: initialRange.endDate,
-    customer: initCustomer || null,
-    display,
+    customer: searchParams.get('customer') || null,
+    display: initialDisplay,
+    integrationIds: integrationsReady
+      ? parseIntegrationIds(
+          searchParams.get('integrationIds'),
+          availableIntegrationIds
+        )
+      : normalizeIntegrationIds(initialRawIntegrationIds),
   });
+  const filtersRef = useRef(filters);
 
-  const params = useMemo(() => {
-    return new URLSearchParams({
-      display: filters.display,
-      startDate: filters.startDate,
-      endDate: filters.endDate,
-      customer: filters?.customer?.toString() || '',
-    }).toString();
-  }, [filters]);
-
-  // Calendar view data fetcher
-  const loadData = useCallback(async () => {
-    const modifiedParams = new URLSearchParams({
-      display: filters.display,
-      customer: filters?.customer?.toString() || '',
-      startDate: newDayjs(filters.startDate).startOf('day').utc().format(),
-      endDate: newDayjs(filters.endDate).endOf('day').utc().format(),
-    }).toString();
-
-    const data = await (await fetch(`/posts?${modifiedParams}`)).json();
-    return expandPosts(data);
-  }, [filters, params]);
-
-  // List view data fetcher
-  const listParams = useMemo(() => {
-    return new URLSearchParams({
-      page: listPage.toString(),
-      limit: '100',
-      customer: filters?.customer?.toString() || '',
-      state: listState,
-    }).toString();
-  }, [listPage, filters.customer, listState]);
-
-  const loadListData = useCallback(async () => {
-    const response = await fetch(`/posts/list?${listParams}`);
-    return expandPostsList(await response.json());
-  }, [listParams]);
-
-  // SWR for calendar view
+  useEffect(() => {
+    if (!integrationsReady) {
+      return;
+    }
+    const integrationIds = normalizeIntegrationIds(
+      pendingUrlIntegrationIds.current || filtersRef.current.integrationIds,
+      availableIntegrationIds
+    );
+    pendingUrlIntegrationIds.current = null;
+    const nextFilters = {
+      ...filtersRef.current,
+      integrationIds,
+    };
+    filtersRef.current = nextFilters;
+    setFilters(() => nextFilters);
+  }, [availableIntegrationIds, integrationsReady]);
+
+  const calendarRequestUrl = useMemo(
+    () =>
+      filters.display === 'list' ? null : buildCalendarRequestUrl(filters),
+    [filters]
+  );
+  const listRequestUrl = useMemo(
+    () =>
+      filters.display === 'list'
+        ? buildCalendarListRequestUrl({
+            page: listPage,
+            limit: 100,
+            state: listState,
+            customer: filters.customer,
+            integrationIds: filters.integrationIds,
+          })
+        : null,
+    [
+      filters.customer,
+      filters.display,
+      filters.integrationIds,
+      listPage,
+      listState,
+    ]
+  );
   const {
     data: calendarData,
+    error: calendarError,
     isLoading: calendarIsLoading,
     mutate: mutateCalendar,
-  } = useSWR(
-    filters.display !== 'list' ? `/posts-${params}` : null,
-    loadData,
-    {
-      refreshInterval: 3600000,
-      refreshWhenOffline: false,
-      refreshWhenHidden: false,
-      revalidateOnFocus: false,
-    }
-  );
-
-  // SWR for list view
+  } = useCalendarPosts(calendarRequestUrl);
   const {
     data: listData,
+    error: listError,
     isLoading: listIsLoading,
     mutate: mutateList,
-  } = useSWR(
-    filters.display === 'list' ? `/posts-list-${listParams}` : null,
-    loadListData,
-    {
-      refreshInterval: 3600000,
-      refreshWhenOffline: false,
-      refreshWhenHidden: false,
-      revalidateOnFocus: false,
-    }
-  );
+  } = useCalendarListPosts(listRequestUrl);
 
   const defaultSign = useCallback(async () => {
     return await (await fetch('/signatures/default')).json();
-  }, []);
-
+  }, [fetch]);
   const setList = useCallback(async () => {
     return (await fetch('/sets')).json();
-  }, []);
-
-  const { data: sets, mutate } = useSWR('sets', setList, {
+  }, [fetch]);
+  const { data: sets } = useSWR('sets', setList, {
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
     revalidateIfStale: false,
@@ -267,72 +247,112 @@ export const CalendarWeekProvider: FC<{
     refreshWhenOffline: false,
   });
 
-  const setFiltersWrapper = useCallback(
-    (newFilters: {
-      startDate: string;
-      endDate: string;
-      display: 'week' | 'month' | 'day' | 'list';
-      customer: string | null;
-    }) => {
-      setDisplaySaved(newFilters.display);
-      setFilters(newFilters);
-      setInternalData([]);
-
-      // Reset page when switching to list view
-      if (newFilters.display === 'list') {
-        setListPage(0);
+  const replaceLaunchesUrl = useCallback(
+    (nextFilters: CalendarFilters, nextListState: ListStateFilter) => {
+      const currentParams = new URLSearchParams(window.location.search);
+      const pendingIntegrationIds = currentParams.get(INTEGRATION_QUERY_PARAM);
+      const params = buildLaunchesSearchParams(
+        currentParams,
+        nextFilters,
+        nextListState
+      );
+      if (pendingUrlIntegrationIds.current !== null) {
+        if (pendingIntegrationIds === null) {
+          params.delete(INTEGRATION_QUERY_PARAM);
+        } else {
+          params.set(INTEGRATION_QUERY_PARAM, pendingIntegrationIds);
+        }
       }
-
-      const path = [
-        `startDate=${newFilters.startDate}`,
-        `endDate=${newFilters.endDate}`,
-        `display=${newFilters.display}`,
-        newFilters.customer ? `customer=${newFilters.customer}` : ``,
-      ].filter((f) => f);
-      window.history.replaceState(null, '', `/launches?${path.join('&')}`);
+      window.history.replaceState(null, '', `/launches?${params.toString()}`);
     },
     []
   );
+  const setFiltersWrapper = useCallback(
+    (update: CalendarFilterUpdate) => {
+      if (update.integrationIds !== undefined) {
+        pendingUrlIntegrationIds.current = null;
+      }
+      const integrationIds =
+        update.integrationIds === undefined
+          ? filtersRef.current.integrationIds
+          : normalizeIntegrationIds(
+              update.integrationIds,
+              integrationsReady ? availableIntegrationIds : undefined
+            );
+      const nextFilters: CalendarFilters = { ...update, integrationIds };
+      filtersRef.current = nextFilters;
+      setDisplaySaved(nextFilters.display);
+      setFilters(() => nextFilters);
+      setInternalData([]);
+      setListPage(0);
+      replaceLaunchesUrl(nextFilters, listStateRef.current);
+    },
+    [
+      availableIntegrationIds,
+      integrationsReady,
+      replaceLaunchesUrl,
+      setDisplaySaved,
+    ]
+  );
+  const setListState = useCallback(
+    (next: ListStateFilter) => {
+      listStateRef.current = next;
+      setListStateRaw(() => next);
+      setListPage(0);
+      replaceLaunchesUrl(filtersRef.current, next);
+    },
+    [replaceLaunchesUrl]
+  );
 
   const posts = useMemo(() => calendarData?.posts || [], [calendarData?.posts]);
-  const comments = useMemo(() => calendarData?.comments || [], [calendarData?.comments]);
-
-  // List view data
+  const comments = useMemo(
+    () => calendarData?.comments || [],
+    [calendarData?.comments]
+  );
   const listPosts = useMemo(() => listData?.posts || [], [listData?.posts]);
-  const listTotal = listData?.total || 0;
-  const listTotalPages = Math.ceil(listTotal / 100);
-
-  const changeDate = useCallback(
-    (id: string, date: dayjs.Dayjs) => {
-      setInternalData((d) =>
-        d.map((post: Post) => {
-          if (post.id === id) {
-            return {
+  const listTotalPages = Math.ceil((listData?.total || 0) / 100);
+  const visibleIntegrations = useMemo(() => {
+    const customerIntegrations = filters.customer
+      ? integrations.filter(
+          (integration) => integration.customer?.id === filters.customer
+        )
+      : integrations;
+    if (!filters.integrationIds.length) {
+      return customerIntegrations;
+    }
+    const selectedIds = new Set(filters.integrationIds);
+    return customerIntegrations.filter((integration) =>
+      selectedIds.has(integration.id)
+    );
+  }, [filters.customer, filters.integrationIds, integrations]);
+
+  const changeDate = useCallback((id: string, date: dayjs.Dayjs) => {
+    setInternalData((current) =>
+      current.map((post) =>
+        post.id === id
+          ? {
               ...post,
               publishDate: date.utc().format('YYYY-MM-DDTHH:mm:ss'),
-            };
-          }
-          return post;
-        })
-      );
-    },
-    [posts, internalData]
-  );
+            }
+          : post
+      )
+    );
+  }, []);
 
   useEffect(() => {
-    if (posts) {
-      setInternalData(posts);
-    }
+    setInternalData(posts);
   }, [posts]);
 
-  // Combined reload function that handles both calendar and list views
   const reloadCalendarView = useCallback(() => {
     mutateCalendar();
     mutateList();
   }, [mutateCalendar, mutateList]);
-
-  // Determine loading state based on current view
-  const loading = filters.display === 'list' ? listIsLoading : calendarIsLoading;
+  const loading =
+    filters.display === 'list' ? listIsLoading : calendarIsLoading;
+  const requestError =
+    filters.display === 'list'
+      ? !!listError && !listData
+      : !!calendarError && !calendarData;
 
   return (
     <CalendarContext.Provider
@@ -342,13 +362,14 @@ export const CalendarWeekProvider: FC<{
         ...filters,
         posts: calendarIsLoading ? [] : internalData,
         loading,
+        requestError,
         integrations,
+        visibleIntegrations,
         setFilters: setFiltersWrapper,
         changeDate,
         comments,
         sets: sets || [],
         signature: sign,
-        // List view specific
         listPosts,
         listPage,
         listTotalPages,
diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx
index 75c60a9..921e5e8 100644
--- a/apps/frontend/src/components/launches/calendar.tsx
+++ b/apps/frontend/src/components/launches/calendar.tsx
@@ -35,7 +35,6 @@ import clsx from 'clsx';
 import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
 import { ExistingDataContextProvider } from '@gitroom/frontend/components/launches/helpers/use.existing.data';
 import { useDrag, useDrop } from 'react-dnd';
-import { Integration, Post, State, Tags } from '@prisma/client';
 import { useAddProvider } from '@gitroom/frontend/components/launches/add.provider.component';
 import { useToaster } from '@gitroom/react/toaster/toaster';
 import { useUser } from '@gitroom/frontend/components/layout/user.context';
@@ -45,19 +44,26 @@ import { groupBy, random, sortBy } from 'lodash';
 import SafeImage from '@gitroom/react/helpers/safe.image';
 import { extend } from 'dayjs';
 import { isUSCitizen } from './helpers/isuscitizen.utils';
-import { useInterval } from '@mantine/hooks';
+import { useInterval, useMediaQuery } from '@mantine/hooks';
 import { StatisticsModal } from '@gitroom/frontend/components/launches/statistics';
 import { MissingReleaseModal } from '@gitroom/frontend/components/launches/missing-release.modal';
 import { useT } from '@gitroom/react/translation/get.transation.service.client';
 import i18next from 'i18next';
 import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal';
-import { CreationMethodBadge } from '@gitroom/frontend/components/launches/creation.method.badge';
 import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
 import { useVariables } from '@gitroom/react/helpers/variable.context';
 import copy from 'copy-to-clipboard';
-import { stripHtmlValidation } from '@gitroom/helpers/utils/strip.html.validation';
 import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone';
 import { Button } from '@gitroom/react/form/button';
+import type {
+  CalendarCardDensity,
+  CalendarPost,
+} from '@gitroom/frontend/components/launches/calendar.types';
+import { CalendarPostCard } from '@gitroom/frontend/components/launches/calendar-post-card';
+import {
+  CompactCalendarView,
+  type CalendarPostActionCallbacks,
+} from '@gitroom/frontend/components/launches/compact-calendar-view';
 
 // Extend dayjs with necessary plugins
 extend(isSameOrAfter);
@@ -94,7 +100,9 @@ export const hours = Array.from(
 );
 
 // Shared hook for post actions (edit, delete, statistics)
-const usePostActions = (onMutate?: () => void) => {
+export const usePostActions = (
+  onMutate?: () => void
+): CalendarPostActionCallbacks => {
   const t = useT();
   const fetch = useFetch();
   const modal = useModals();
@@ -117,9 +125,7 @@ const usePostActions = (onMutate?: () => void) => {
       const date = !isDuplicate
         ? null
         : (await (await fetch('/posts/find-slot')).json()).date;
-      const publishDate = dayjs
-        .utc(date || data.posts[0].publishDate)
-        .local();
+      const publishDate = dayjs.utc(date || data.posts[0].publishDate).local();
       const ExistingData = !isDuplicate
         ? ExistingDataContextProvider
         : Fragment;
@@ -248,21 +254,34 @@ const usePostActions = (onMutate?: () => void) => {
         classNames: {
           modal: 'w-[100%] max-w-[800px]',
         },
-        children: (
-          <MissingReleaseModal postId={id} onSuccess={mutate} />
-        ),
+        children: <MissingReleaseModal postId={id} onSuccess={mutate} />,
         size: '60%',
       });
     },
     [modal, t, mutate]
   );
 
-  return { editPost, deletePost, copyDebugJson, openStatistics, openMissingRelease };
+  const previewPost = useCallback(
+    (post: CalendarPost) => () => {
+      window.open(`/p/${post.id}?share=true`, '_blank');
+    },
+    []
+  );
+
+  return {
+    editPost,
+    duplicatePost: (post) => editPost(post, true),
+    deletePost,
+    copyDebugJson,
+    statistics: (post) => openStatistics(post.id),
+    missingRelease: (post) => openMissingRelease(post.id),
+    previewPost,
+  };
 };
 
 export const DayView = () => {
   const calendar = useCalendar();
-  const { integrations, posts, startDate } = calendar;
+  const { visibleIntegrations, posts, startDate } = calendar;
 
   // Set dayjs locale based on current language
   const currentLanguage = i18next.resolvedLanguage || 'en';
@@ -271,22 +290,31 @@ export const DayView = () => {
   const currentDay = dayjs.utc(startDate);
 
   const options = useMemo(() => {
-    const createdPosts = posts.map((post) => ({
-      integration: [integrations.find((i) => i.id === post.integration.id)!],
-      image: post?.integration?.picture || '',
-      identifier: post?.integration?.providerIdentifier || '',
-      id: post?.integration?.id || '',
-      name: post?.integration?.name || '',
-      time: dayjs
-        .utc(post.publishDate)
-        .diff(dayjs.utc(post.publishDate).startOf('day'), 'minute'),
-    }));
+    const createdPosts = posts.flatMap((post) => {
+      const integration = visibleIntegrations.find(
+        (item) => item.id === post.integration.id
+      );
+      return integration
+        ? [
+            {
+              integration: [integration],
+              image: post?.integration?.picture || '',
+              identifier: post?.integration?.providerIdentifier || '',
+              id: post?.integration?.id || '',
+              name: post?.integration?.name || '',
+              time: dayjs
+                .utc(post.publishDate)
+                .diff(dayjs.utc(post.publishDate).startOf('day'), 'minute'),
+            },
+          ]
+        : [];
+    });
     return sortBy(
       Object.values(
         groupBy(
           [
             ...createdPosts,
-            ...integrations.flatMap((p) =>
+            ...visibleIntegrations.flatMap((p) =>
               p.time.flatMap((t) => ({
                 integration: p,
                 identifier: p?.identifier,
@@ -302,7 +330,7 @@ export const DayView = () => {
       ),
       (p) => p[0].time
     );
-  }, [integrations, posts]);
+  }, [visibleIntegrations, posts]);
 
   return (
     <div className="flex flex-col gap-[10px] flex-1 relative">
@@ -321,13 +349,9 @@ export const DayView = () => {
               key={option[0].time}
               className="min-h-[60px] rounded-[10px] flex justify-center items-center gap-[10px] mb-[20px]"
             >
-              <CalendarContext.Provider
-                value={{
-                  ...calendar,
-                  integrations: option.flatMap((p) => p.integration),
-                }}
-              >
+              <CalendarContext.Provider value={calendar}>
                 <CalendarColumn
+                  slotIntegrations={option.flatMap((p) => p.integration)}
                   getDate={currentDay
                     .startOf('day')
                     .add(option[0].time, 'minute')
@@ -493,7 +517,8 @@ export const MonthView = () => {
 export const ListView = () => {
   const t = useT();
   const user = useUser();
-  const { integrations, loading, listPosts, listState } = useCalendar();
+  const calendar = useCalendar();
+  const { integrations, loading, listPosts, listState } = calendar;
   const emptyMessage =
     listState === 'scheduled'
       ? t('no_upcoming_posts', 'No upcoming posts scheduled')
@@ -504,7 +529,15 @@ export const ListView = () => {
       : t('no_posts', 'No posts');
 
   // Use shared post actions hook
-  const { editPost, deletePost, copyDebugJson, openStatistics, openMissingRelease } = usePostActions();
+  const {
+    editPost,
+    duplicatePost,
+    deletePost,
+    copyDebugJson,
+    statistics,
+    missingRelease,
+    previewPost,
+  } = usePostActions();
 
   // Group posts by date
   const groupedPosts = useMemo(() => {
@@ -530,7 +563,28 @@ export const ListView = () => {
   if (listPosts.length === 0) {
     return (
       <div className="flex flex-col flex-1 items-center justify-center">
-        <div className="text-textColor text-[16px]">{emptyMessage}</div>
+        <div className="text-textColor text-[16px]">
+          {calendar.integrationIds.length > 0
+            ? t('calendar_no_channel_posts', 'No posts match these channels')
+            : emptyMessage}
+        </div>
+        {calendar.integrationIds.length > 0 && (
+          <button
+            type="button"
+            className="mt-[10px] rounded-[8px] border border-newSep px-[12px] py-[7px] text-[13px] text-textColor focus-visible:ring-2 focus-visible:ring-btnPrimary"
+            onClick={() =>
+              calendar.setFilters({
+                startDate: calendar.startDate,
+                endDate: calendar.endDate,
+                display: calendar.display,
+                customer: calendar.customer,
+                integrationIds: [],
+              })
+            }
+          >
+            {t('calendar_show_all_channels', 'Show all channels')}
+          </button>
+        )}
       </div>
     );
   }
@@ -541,21 +595,25 @@ export const ListView = () => {
         {groupedPosts.map(([dateKey, datePosts]) => (
           <Fragment key={dateKey}>
             <div className="text-center text-[14px] min-h-[21px] text-textColor font-[500] mt-[10px]">
-              {newDayjs(dateKey).format(isUSCitizen() ? 'dddd, MMMM D, YYYY' : 'dddd, D MMMM YYYY')}
+              {newDayjs(dateKey).format(
+                isUSCitizen() ? 'dddd, MMMM D, YYYY' : 'dddd, D MMMM YYYY'
+              )}
             </div>
             <div className="flex flex-col gap-[10px] mb-[20px] px-[10px]">
               {datePosts.map((post) => (
                 <CalendarItem
                   key={post.id}
-                  display="day"
+                  display="list"
                   isBeforeNow={false}
                   date={newDayjs(post.publishDate)}
-                  state={post.state}
-                  statistics={openStatistics(post.id)}
-                  missingRelease={openMissingRelease(post.id)}
-                  editPost={editPost(post, false)}
-                  duplicatePost={editPost(post, true)}
-                  copyDebugJson={user?.isSuperAdmin ? copyDebugJson(post) : undefined}
+                  statistics={statistics(post)}
+                  missingRelease={missingRelease(post)}
+                  editPost={editPost(post)}
+                  duplicatePost={duplicatePost(post)}
+                  previewPost={previewPost(post)}
+                  copyDebugJson={
+                    user?.isSuperAdmin ? copyDebugJson(post) : undefined
+                  }
                   post={post}
                   integrations={integrations}
                   deletePost={deletePost(post)}
@@ -570,13 +628,63 @@ export const ListView = () => {
   );
 };
 
+const CompactCalendar = () => {
+  const calendar = useCalendar();
+  const postActions = usePostActions();
+
+  return (
+    <CompactCalendarView
+      posts={calendar.posts}
+      loading={calendar.loading}
+      integrationIds={calendar.integrationIds}
+      onClearIntegrations={() =>
+        calendar.setFilters({
+          startDate: calendar.startDate,
+          endDate: calendar.endDate,
+          display: calendar.display,
+          customer: calendar.customer,
+          integrationIds: [],
+        })
+      }
+      postActions={postActions}
+    />
+  );
+};
+
 export const Calendar = () => {
-  const { display } = useCalendar();
+  const { display, requestError, reloadCalendarView } = useCalendar();
+  const t = useT();
+  const isCompact = useMediaQuery('(max-width: 1025px)');
+
+  if (requestError) {
+    return (
+      <div
+        role="alert"
+        className="flex flex-1 flex-col items-center justify-center gap-[12px] text-center text-textColor"
+      >
+        <p>{t('calendar_request_error', 'Unable to load the calendar.')}</p>
+        <button
+          type="button"
+          onClick={reloadCalendarView}
+          className="rounded-[8px] bg-btnPrimary px-[14px] py-[8px] text-[14px] font-[500] text-white focus-visible:ring-2 focus-visible:ring-btnPrimary focus-visible:ring-offset-2 focus-visible:ring-offset-newBgColorInner"
+        >
+          {t('calendar_retry', 'Retry')}
+        </button>
+      </div>
+    );
+  }
+
+  if (display === 'list') {
+    return <ListView />;
+  }
+
+  if (isCompact) {
+    return <CompactCalendar />;
+  }
+
   return (
     <>
-      {display === 'list' ? (
-        <ListView />
-      ) : display === 'day' ? (
+      {display === 'day' ? (
         <DayView />
       ) : display === 'week' ? (
         <WeekView />
@@ -589,10 +697,11 @@ export const Calendar = () => {
 export const CalendarColumn: FC<{
   getDate: dayjs.Dayjs;
   randomHour?: boolean;
+  slotIntegrations?: Integrations[];
 }> = memo((props) => {
   const t = useT();
 
-  const { getDate, randomHour } = props;
+  const { getDate, randomHour, slotIntegrations } = props;
   const [num, setNum] = useState(0);
   const user = useUser();
   const {
@@ -605,13 +714,33 @@ export const CalendarColumn: FC<{
     signature,
     loading,
   } = useCalendar();
+  const displayIntegrations = slotIntegrations || integrations;
   const modal = useModals();
   const fetch = useFetch();
+  const currentSignature = signature as
+    | { id?: string; content?: string }
+    | undefined;
 
   // Use shared post actions hook
-  const { editPost, deletePost, copyDebugJson, openStatistics, openMissingRelease } = usePostActions();
+  const {
+    editPost,
+    duplicatePost,
+    deletePost,
+    copyDebugJson,
+    statistics,
+    missingRelease,
+    previewPost,
+  } = usePostActions();
   const postList = useMemo(() => {
     return posts.filter((post) => {
+      if (
+        slotIntegrations &&
+        !displayIntegrations.some(
+          (integration) => integration.id === post.integration.id
+        )
+      ) {
+        return false;
+      }
       const pList = dayjs.utc(post.publishDate).local();
       const check =
         display === 'day'
@@ -623,7 +752,7 @@ export const CalendarColumn: FC<{
           : pList.format('DD/MM/YYYY') === getDate.format('DD/MM/YYYY');
       return check;
     });
-  }, [posts, display, getDate]);
+  }, [posts, display, getDate, slotIntegrations, displayIntegrations]);
   const [showAll, setShowAll] = useState(false);
   const showAllFunc = useCallback(() => {
     setShowAll(true);
@@ -661,94 +790,103 @@ export const CalendarColumn: FC<{
       stop();
     };
   }, []);
-  const [{ canDrop }, drop] = useDrop(() => ({
-    accept: 'post',
-    drop: async (item: any) => {
-      if (isBeforeNow) return;
+  const [{ canDrop }, drop] = useDrop(
+    () => ({
+      accept: 'post',
+      drop: async (item: any) => {
+        if (isBeforeNow) return;
 
-      // Find the post to check its state
-      const post = posts.find((p) => p.id === item.id);
-      let action: 'schedule' | 'update' = 'schedule';
+        // Find the post to check its state
+        const post = posts.find((p) => p.id === item.id);
+        let action: 'schedule' | 'update' = 'schedule';
 
-      // Check if post is already published or queued in the past
-      if (
-        post &&
-        (post.state === 'PUBLISHED' ||
-          (post.state === 'QUEUE' && dayjs().isAfter(dayjs.utc(post.publishDate))))
-      ) {
-        const whatToDo = await new Promise<'schedule' | 'update' | 'cancel'>(
-          (resolve) => {
-            modal.openModal({
-              title: t('what_do_you_want_to_do', 'What do you want to do?'),
-              children: (
-                <div className="flex flex-col">
-                  <div className="text-[20px] mb-[20px]">
-                    {t(
-                      'post_already_published_drag',
-                      'This post was already published, what do you want to do?'
-                    )}
-                  </div>
-                  <div className="flex w-full gap-[10px]">
-                    <div className="flex-1 flex">
-                      <Button
-                        type="button"
-                        className="flex-1"
-                        onClick={() => {
-                          modal.closeAll();
-                          resolve('update');
-                        }}
-                      >
-                        {t('just_update_post_details', 'Just update the post details')}
-                      </Button>
+        // Check if post is already published or queued in the past
+        if (
+          post &&
+          (post.state === 'PUBLISHED' ||
+            (post.state === 'QUEUE' &&
+              dayjs().isAfter(dayjs.utc(post.publishDate))))
+        ) {
+          const whatToDo = await new Promise<'schedule' | 'update' | 'cancel'>(
+            (resolve) => {
+              modal.openModal({
+                title: t('what_do_you_want_to_do', 'What do you want to do?'),
+                children: (
+                  <div className="flex flex-col">
+                    <div className="text-[20px] mb-[20px]">
+                      {t(
+                        'post_already_published_drag',
+                        'This post was already published, what do you want to do?'
+                      )}
                     </div>
-                    <div className="flex-1 flex">
-                      <Button
-                        type="button"
-                        className="flex-1"
-                        onClick={() => {
-                          modal.closeAll();
-                          resolve('schedule');
-                        }}
-                      >
-                        {t('reschedule_post', 'Reschedule the post')}
-                      </Button>
+                    <div className="flex w-full gap-[10px]">
+                      <div className="flex-1 flex">
+                        <Button
+                          type="button"
+                          className="flex-1"
+                          onClick={() => {
+                            modal.closeAll();
+                            resolve('update');
+                          }}
+                        >
+                          {t(
+                            'just_update_post_details',
+                            'Just update the post details'
+                          )}
+                        </Button>
+                      </div>
+                      <div className="flex-1 flex">
+                        <Button
+                          type="button"
+                          className="flex-1"
+                          onClick={() => {
+                            modal.closeAll();
+                            resolve('schedule');
+                          }}
+                        >
+                          {t('reschedule_post', 'Reschedule the post')}
+                        </Button>
+                      </div>
                     </div>
                   </div>
-                </div>
-              ),
-              onClose: () => resolve('cancel'),
-            });
-          }
-        );
+                ),
+                onClose: () => resolve('cancel'),
+              });
+            }
+          );
 
-        if (whatToDo === 'cancel') {
-          return;
+          if (whatToDo === 'cancel') {
+            return;
+          }
+          action = whatToDo;
         }
-        action = whatToDo;
-      }
 
-      if (!item.interval) {
-        changeDate(item.id, getDate);
-      }
-      const { status } = await fetch(`/posts/${item.id}/date`, {
-        method: 'PUT',
-        body: JSON.stringify({
-          date: getDate.utc().format('YYYY-MM-DDTHH:mm:ss'),
-          action,
-        }),
-      });
-      if (status !== 500) {
-        if (item.interval || action === 'schedule') {
-          reloadCalendarView();
+        if (!item.interval) {
+          changeDate(item.id, getDate);
+        }
+        const { status } = await fetch(`/posts/${item.id}/date`, {
+          method: 'PUT',
+          body: JSON.stringify({
+            date: getDate.utc().format('YYYY-MM-DDTHH:mm:ss'),
+            action,
+          }),
+        });
+        if (status !== 500) {
+          if (item.interval || action === 'schedule') {
+            reloadCalendarView();
+            return;
+          }
           return;
         }
-        return;
-      }
-    },
-    collect: (monitor) => ({
-      canDrop: isBeforeNow ? false : !!monitor.canDrop() && !!monitor.isOver(),
+      },
+      collect: (monitor) => ({
+        canDrop: isBeforeNow
+          ? false
+          : !!monitor.canDrop() && !!monitor.isOver(),
+      }),
     }),
-  }), [posts]);
+    [posts]
+  );
 
   const addModal = useCallback(async () => {
     const set: any = !sets.length
@@ -799,11 +937,11 @@ export const CalendarColumn: FC<{
             ...p,
           }))}
           mutate={reloadCalendarView}
-          {...(signature?.id && !set
+          {...(currentSignature?.id && !set
             ? {
                 onlyValues: [
                   {
-                    content: '\n' + signature.content,
+                    content: '\n' + currentSignature.content,
                   },
                 ],
               }
@@ -870,15 +1008,18 @@ export const CalendarColumn: FC<{
                   display={display as 'day' | 'week' | 'month'}
                   isBeforeNow={isBeforeNow}
                   date={getDate}
-                  state={post.state}
-                  statistics={openStatistics(post.id)}
-                  missingRelease={openMissingRelease(post.id)}
-                  editPost={editPost(post, false)}
-                  duplicatePost={editPost(post, true)}
-                  copyDebugJson={user?.isSuperAdmin ? copyDebugJson(post) : undefined}
+                  statistics={statistics(post)}
+                  missingRelease={missingRelease(post)}
+                  editPost={editPost(post)}
+                  duplicatePost={duplicatePost(post)}
+                  previewPost={previewPost(post)}
+                  copyDebugJson={
+                    user?.isSuperAdmin ? copyDebugJson(post) : undefined
+                  }
                   post={post}
-                  integrations={integrations}
+                  integrations={displayIntegrations}
                   deletePost={deletePost(post)}
+                  showTime={true}
                 />
               </div>
             </div>
@@ -903,7 +1044,7 @@ export const CalendarColumn: FC<{
         {!isBeforeNow && (
           <div
             className="pb-[2.5px] px-[5px] flex-1 flex"
-            onClick={integrations.length ? addModal : addProvider}
+            onClick={displayIntegrations.length ? addModal : addProvider}
           >
             <div
               className={clsx(
@@ -930,7 +1071,7 @@ export const CalendarColumn: FC<{
                 <div
                   className={`w-full h-full rounded-[10px] py-[10px] flex-wrap hover:border hover:border-seventh flex justify-center items-center gap-[20px] opacity-30 grayscale hover:grayscale-0 hover:opacity-100`}
                 >
-                  {integrations.map((selectedIntegrations) => (
+                  {displayIntegrations.map((selectedIntegrations) => (
                     <div
                       className="relative"
                       key={selectedIntegrations.identifier}
@@ -981,45 +1122,32 @@ const CalendarItem: FC<{
   isBeforeNow: boolean;
   editPost: () => void;
   duplicatePost: () => void;
+  previewPost: () => void;
   copyDebugJson?: () => void;
   deletePost: () => void;
   statistics: () => void;
   missingRelease?: () => void;
   integrations: Integrations[];
-  state: State;
-  display: 'day' | 'week' | 'month';
+  display: CalendarCardDensity;
   showTime?: boolean;
-  post: Post & {
-    integration: Integration;
-    tags: {
-      tag: Tags;
-    }[];
-  };
+  post: CalendarPost;
 }> = memo((props) => {
-  const t = useT();
   const {
     editPost,
     statistics,
     duplicatePost,
+    previewPost,
     copyDebugJson,
     post,
     date,
     isBeforeNow,
-    state,
     display,
     deletePost,
     showTime,
     missingRelease,
+    integrations,
   } = props;
   const { disableXAnalytics } = useVariables();
-  const user = useUser();
-  const showCreationMethodBadge =
-    user?.impersonate &&
-    post.creationMethod &&
-    post.creationMethod !== 'UNKNOWN';
-  const preview = useCallback(() => {
-    window.open(`/p/` + post.id + '?share=true', '_blank');
-  }, [post]);
   const [{ opacity }, dragRef] = useDrag(
     () => ({
       type: 'post',
@@ -1034,252 +1162,48 @@ const CalendarItem: FC<{
     }),
     []
   );
+  const dragTargetRef = useCallback(
+    (element: HTMLElement | null) => {
+      dragRef(element);
+    },
+    [dragRef]
+  );
+  const integrationState = integrations.find(
+    (integration) => integration.id === post.integration.id
+  );
+  const canShowStatistics = !(
+    (post.integration.providerIdentifier === 'x' && disableXAnalytics) ||
+    !post.releaseId
+  );
+
   return (
     <div
-      // @ts-ignore
-      ref={dragRef}
-      className={clsx(
-        'w-full flex h-full flex-1 flex-col group',
-        'relative',
-        state === 'ERROR' && 'rounded-[10px] ring-2 ring-red-500'
-      )}
+      className="relative flex h-full w-full flex-1 flex-col"
       style={{
         opacity,
       }}
     >
-      {state === 'ERROR' && (
-        <div
-          className="absolute -top-[6px] -left-[6px] z-20 w-[18px] h-[18px] rounded-full bg-red-500 flex items-center justify-center text-white text-[11px] font-bold cursor-pointer"
-          data-tooltip-id="tooltip"
-          data-tooltip-content={post.error || 'An error occurred while publishing this post'}
-        >
-          !
-        </div>
-      )}
-      {showCreationMethodBadge && (
-        <div className="absolute -bottom-[4px] -right-[4px] z-10">
-          <CreationMethodBadge
-            creationMethod={post.creationMethod}
-            ringColor="var(--new-bgColor)"
-          />
-        </div>
-      )}
-      <div
-        className={clsx(
-          'text-white text-[11px] max-h-[24px] h-[24px] min-h-[24px] w-full rounded-tr-[10px] rounded-tl-[10px] flex items-center justify-center gap-[10px] px-[5px] bg-btnPrimary'
-        )}
-        style={{
-          backgroundColor: post?.tags?.[0]?.tag?.color,
-        }}
-      >
-        <div
-          className={clsx(
-            post?.tags?.[0]?.tag?.color ? 'mix-blend-difference' : '',
-            'group-hover:hidden cursor-pointer'
-          )}
-        >
-          {post.tags.map((p) => p.tag.name).join(', ')}
-        </div>
-        {copyDebugJson && (
-          <div
-            className={clsx(
-              'hidden group-hover:block hover:underline cursor-pointer',
-              post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-            )}
-            onClick={copyDebugJson}
-          >
-            <CopyDebug />
-          </div>
-        )}
-        <div
-          className={clsx(
-            'hidden group-hover:block hover:underline cursor-pointer',
-            post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-          )}
-          onClick={duplicatePost}
-        >
-          <Duplicate />
-        </div>
-        <div
-          className={clsx(
-            'hidden group-hover:block hover:underline cursor-pointer',
-            post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-          )}
-          onClick={preview}
-        >
-          <Preview />
-        </div>{' '}
-        {((post.integration.providerIdentifier === 'x' && disableXAnalytics) || !post.releaseId) ? (
-          <></>
-        ) : post.releaseId === 'missing' && missingRelease ? (
-          <div
-            className={clsx(
-              'hidden group-hover:block hover:underline cursor-pointer',
-              post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-            )}
-            onClick={missingRelease}
-          >
-            <Statistics />
-          </div>
-        ) : post.releaseId !== 'missing' ? (
-          <div
-            className={clsx(
-              'hidden group-hover:block hover:underline cursor-pointer',
-              post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-            )}
-            onClick={statistics}
-          >
-            <Statistics />
-          </div>
-        ) : (
-          <></>
-        )}{' '}
-        <div
-          className={clsx(
-            'hidden group-hover:block hover:underline cursor-pointer',
-            post?.tags?.[0]?.tag?.color && 'mix-blend-difference'
-          )}
-          onClick={deletePost}
-        >
-          <DeletePost />
-        </div>
-      </div>
-      <div
-        onClick={editPost}
-        className={clsx(
-          'gap-[5px] w-full flex h-full flex-1 rounded-br-[10px] rounded-bl-[10px] p-[8px] text-[14px] bg-newColColor',
-          'relative',
-          isBeforeNow && '!grayscale'
-        )}
-      >
-        <div className={clsx('relative min-w-[20px]')}>
-          <img
-            className="w-[20px] h-[20px] rounded-[8px]"
-            src={post.integration.picture! || '/no-picture.jpg'}
-          />
-          <img
-            className="w-[12px] h-[12px] rounded-[8px] absolute z-10 top-[10px] end-0 border border-fifth"
-            src={`/icons/platforms/${post.integration?.providerIdentifier}.png`}
-          />
-        </div>
-        <div className="w-full flex-1 flex flex-col min-h-[40px]">
-          <div className="text-start">
-            {state === 'DRAFT' ? t('draft', 'Draft') + ': ' : ''}
-          </div>
-            <div className="w-full relative">
-              <div className="absolute top-0 start-0 w-full text-ellipsis break-words line-clamp-1 text-start">
-                {stripHtmlValidation('none', post.content, false, true, false) ||
-                  t('no_content', 'no content')}
-              </div>
-            </div>
-        </div>
-        {showTime && (
-          <div className="text-textColor/50 text-[12px] whitespace-nowrap flex items-center">
-            {newDayjs(post.publishDate).local().format(isUSCitizen() ? 'hh:mm A' : 'HH:mm')}
-          </div>
-        )}
-      </div>
+      <CalendarPostCard
+        post={post}
+        density={display}
+        date={showTime ? newDayjs(post.publishDate) : date}
+        isPast={isBeforeNow}
+        showTime={showTime}
+        integrationState={integrationState}
+        canShowStatistics={canShowStatistics}
+        canCopyDebugJson={!!copyDebugJson}
+        editPost={editPost}
+        duplicatePost={duplicatePost}
+        previewPost={previewPost}
+        deletePost={deletePost}
+        statistics={statistics}
+        missingRelease={missingRelease}
+        copyDebugJson={copyDebugJson}
+        dragTargetRef={dragTargetRef}
+      />
     </div>
   );
 });
-const CopyDebug = () => {
-  const t = useT();
-  return (
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      width="15"
-      height="15"
-      viewBox="0 0 24 24"
-      fill="none"
-      stroke="currentColor"
-      strokeWidth="2"
-      strokeLinecap="round"
-      strokeLinejoin="round"
-      data-tooltip-id="tooltip"
-      data-tooltip-content={t('copy_debug_json', 'Copy Debug JSON')}
-    >
-      <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
-      <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
-    </svg>
-  );
-};
-const Duplicate = () => {
-  const t = useT();
-  return (
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      width="15"
-      height="15"
-      viewBox="0 0 32 32"
-      fill="none"
-      data-tooltip-id="tooltip"
-      data-tooltip-content={t('duplicate_post', 'Duplicate Post')}
-    >
-      <path
-        d="M27 5H9C8.46957 5 7.96086 5.21071 7.58579 5.58579C7.21071 5.96086 7 6.46957 7 7V9H5C4.46957 9 3.96086 9.21071 3.58579 9.58579C3.21071 9.96086 3 10.4696 3 11V25C3 25.5304 3.21071 26.0391 3.58579 26.4142C3.96086 26.7893 4.46957 27 5 27H23C23.5304 27 24.0391 26.7893 24.4142 26.4142C24.7893 26.0391 25 25.5304 25 25V23H27C27.5304 23 28.0391 22.7893 28.4142 22.4142C28.7893 22.0391 29 21.5304 29 21V7C29 6.46957 28.7893 5.96086 28.4142 5.58579C28.0391 5.21071 27.5304 5 27 5ZM23 11V13H5V11H23ZM23 25H5V15H23V25ZM27 21H25V11C25 10.4696 24.7893 9.96086 24.4142 9.58579C24.0391 9.21071 23.5304 9 23 9H9V7H27V21Z"
-        fill="currentColor"
-      />
-    </svg>
-  );
-};
-const Preview = () => {
-  const t = useT();
-  return (
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      width="15"
-      height="15"
-      viewBox="0 0 32 32"
-      fill="none"
-      data-tooltip-id="tooltip"
-      data-tooltip-content={t('preview_post', 'Preview Post')}
-    >
-      <path
-        d="M30.9137 15.595C30.87 15.4963 29.8112 13.1475 27.4575 10.7937C24.3212 7.6575 20.36 6 16 6C11.64 6 7.67874 7.6575 4.54249 10.7937C2.18874 13.1475 1.12499 15.5 1.08624 15.595C1.02938 15.7229 1 15.8613 1 16.0012C1 16.1412 1.02938 16.2796 1.08624 16.4075C1.12999 16.5062 2.18874 18.8538 4.54249 21.2075C7.67874 24.3425 11.64 26 16 26C20.36 26 24.3212 24.3425 27.4575 21.2075C29.8112 18.8538 30.87 16.5062 30.9137 16.4075C30.9706 16.2796 31 16.1412 31 16.0012C31 15.8613 30.9706 15.7229 30.9137 15.595ZM16 24C12.1525 24 8.79124 22.6012 6.00874 19.8438C4.86704 18.7084 3.89572 17.4137 3.12499 16C3.89551 14.5862 4.86686 13.2915 6.00874 12.1562C8.79124 9.39875 12.1525 8 16 8C19.8475 8 23.2087 9.39875 25.9912 12.1562C27.1352 13.2912 28.1086 14.5859 28.8812 16C27.98 17.6825 24.0537 24 16 24ZM16 10C14.8133 10 13.6533 10.3519 12.6666 11.0112C11.6799 11.6705 10.9108 12.6075 10.4567 13.7039C10.0026 14.8003 9.88377 16.0067 10.1153 17.1705C10.3468 18.3344 10.9182 19.4035 11.7573 20.2426C12.5965 21.0818 13.6656 21.6532 14.8294 21.8847C15.9933 22.1162 17.1997 21.9974 18.2961 21.5433C19.3924 21.0892 20.3295 20.3201 20.9888 19.3334C21.6481 18.3467 22 17.1867 22 16C21.9983 14.4092 21.3657 12.884 20.2408 11.7592C19.1159 10.6343 17.5908 10.0017 16 10ZM16 20C15.2089 20 14.4355 19.7654 13.7777 19.3259C13.1199 18.8864 12.6072 18.2616 12.3045 17.5307C12.0017 16.7998 11.9225 15.9956 12.0768 15.2196C12.2312 14.4437 12.6122 13.731 13.1716 13.1716C13.731 12.6122 14.4437 12.2312 15.2196 12.0769C15.9956 11.9225 16.7998 12.0017 17.5307 12.3045C18.2616 12.6072 18.8863 13.1199 19.3259 13.7777C19.7654 14.4355 20 15.2089 20 16C20 17.0609 19.5786 18.0783 18.8284 18.8284C18.0783 19.5786 17.0609 20 16 20Z"
-        fill="currentColor"
-      />
-    </svg>
-  );
-};
-export const Statistics = () => {
-  const t = useT();
-  return (
-    <svg
-      xmlns="http://www.w3.org/2000/svg"
-      width="15"
-      height="15"
-      viewBox="0 0 32 32"
-      fill="none"
-      data-tooltip-id="tooltip"
-      data-tooltip-content={t('post_statistics', 'Post Statistics')}
-    >
-      <path
-        d="M28 25H27V5C27 4.73478 26.8946 4.48043 26.7071 4.29289C26.5196 4.10536 26.2652 4 26 4H19C18.7348 4 18.4804 4.10536 18.2929 4.29289C18.1054 4.48043 18 4.73478 18 5V10H12C11.7348 10 11.4804 10.1054 11.2929 10.2929C11.1054 10.4804 11 10.7348 11 11V16H6C5.73478 16 5.48043 16.1054 5.29289 16.2929C5.10536 16.4804 5 16.7348 5 17V25H4C3.73478 25 3.48043 25.1054 3.29289 25.2929C3.10536 25.4804 3 25.7348 3 26C3 26.2652 3.10536 26.5196 3.29289 26.7071C3.48043 26.8946 3.73478 27 4 27H28C28.2652 27 28.5196 26.8946 28.7071 26.7071C28.8946 26.5196 29 26.2652 29 26C29 25.7348 28.8946 25.4804 28.7071 25.2929C28.5196 25.1054 28.2652 25 28 25ZM20 6H25V25H20V6ZM13 12H18V25H13V12ZM7 18H11V25H7V18Z"
-        fill="currentColor"
-      />
-    </svg>
-  );
-};
-
-export const DeletePost = () => {
-  const t = useT();
-  return (
-    <svg
-      width="15"
-      height="15"
-      viewBox="0 0 24 24"
-      fill="none"
-      xmlns="http://www.w3.org/2000/svg"
-      data-tooltip-id="tooltip"
-      data-tooltip-content={t('delete_post', 'Delete Post')}
-    >
-      <path
-        d="M15 10V18H9V10H15ZM14 4H9.9L8.9 5H6V7H18V5H15L14 4ZM17 8H7V18C7 19.1 7.9 20 9 20H15C16.1 20 17 19.1 17 18V8Z"
-        fill="currentColor"
-      />
-    </svg>
-  );
-};
 
 export const SetSelectionModal: FC<{
   sets: any[];
diff --git a/apps/frontend/src/components/launches/filters.tsx b/apps/frontend/src/components/launches/filters.tsx
index 5fd690f..545aa84 100644
--- a/apps/frontend/src/components/launches/filters.tsx
+++ b/apps/frontend/src/components/launches/filters.tsx
@@ -1,10 +1,15 @@
 'use client';
 
-import { useCalendar, ListStateFilter } from '@gitroom/frontend/components/launches/calendar.context';
+import {
+  useCalendar,
+  ListStateFilter,
+} from '@gitroom/frontend/components/launches/calendar.context';
 import clsx from 'clsx';
 import dayjs from 'dayjs';
 import { useCallback } from 'react';
 import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer';
+import { IntegrationFilter } from '@gitroom/frontend/components/launches/integration-filter';
+import { normalizeIntegrationIds } from '@gitroom/frontend/components/launches/calendar-filter-query';
 import { useT } from '@gitroom/react/translation/get.transation.service.client';
 import i18next from 'i18next';
 import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone';
@@ -170,15 +175,40 @@ export const Filters = () => {
   }, [calendar]);
 
   const setCustomer = useCallback(
-    (customer: string) => {
+    (customer: string | null) => {
       if (calendar.customer === customer) {
         return; // No need to set the same customer
       }
+      const customerIntegrationIds = customer
+        ? new Set(
+            calendar.integrations
+              .filter((integration) => integration.customer?.id === customer)
+              .map(({ id }) => id)
+          )
+        : undefined;
+      const integrationIds = normalizeIntegrationIds(
+        calendar.integrationIds,
+        customerIntegrationIds
+      );
       calendar.setFilters({
         startDate: calendar.startDate,
         endDate: calendar.endDate,
         display: calendar.display as 'day' | 'week' | 'month',
-        customer: customer,
+        customer,
+        integrationIds,
+      });
+    },
+    [calendar]
+  );
+
+  const setIntegrations = useCallback(
+    (integrationIds: string[]) => {
+      calendar.setFilters({
+        startDate: calendar.startDate,
+        endDate: calendar.endDate,
+        display: calendar.display,
+        customer: calendar.customer,
+        integrationIds,
       });
     },
     [calendar]
@@ -287,13 +317,24 @@ export const Filters = () => {
   }, [calendar]);
 
   return (
-    <div className="text-textColor flex flex-col md:flex-row gap-[8px] items-center select-none">
+    <div
+      data-testid="calendar-filters"
+      className="flex flex-col items-center gap-[8px] text-textColor md:flex-row tablet:items-stretch tablet:!flex-col select-none"
+    >
       {!isListView && (
-        <div className="flex flex-grow flex-row items-center gap-[10px]">
-          <div className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden">
-            <div
+        <div
+          data-testid="calendar-primary-controls"
+          className="flex flex-grow flex-row items-center gap-[10px] tablet:w-full tablet:flex-wrap"
+        >
+          <div
+            data-testid="calendar-date-navigation"
+            className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden tablet:w-full"
+          >
+            <button
+              type="button"
+              aria-label={t('previous_period', 'Previous period')}
               onClick={previous}
-              className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused"
+              className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused focus-visible:ring-2 focus-visible:ring-btnPrimary"
             >
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -310,15 +351,17 @@ export const Filters = () => {
                   strokeLinejoin="round"
                 />
               </svg>
-            </div>
-            <div className="min-w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center">
+            </button>
+            <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center tablet:w-auto tablet:min-w-0 tablet:flex-1">
               <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]">
                 {getDisplayText()}
               </div>
             </div>
-            <div
+            <button
+              type="button"
+              aria-label={t('next_period', 'Next period')}
               onClick={next}
-              className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused"
+              className="cursor-pointer text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center hover:text-textItemFocused hover:bg-boxFocused focus-visible:ring-2 focus-visible:ring-btnPrimary"
             >
               <svg
                 xmlns="http://www.w3.org/2000/svg"
@@ -335,24 +378,34 @@ export const Filters = () => {
                   strokeLinejoin="round"
                 />
               </svg>
-            </div>
+            </button>
           </div>
           <div className="flex-1 text-[14px] font-[500]">
             <div className="text-center flex h-[42px]">
-              <div
+              <button
+                type="button"
                 onClick={setToday}
                 className="hover:text-textItemFocused hover:bg-boxFocused py-[3px] px-[9px] flex justify-center items-center rounded-[8px] transition-all cursor-pointer text-[14px] bg-newBgColorInner border border-newTableBorder"
               >
                 {t('today', 'Today')}
-              </div>
+              </button>
             </div>
           </div>
         </div>
       )}
       {isListView && (
-        <div className="flex flex-grow flex-row items-center gap-[10px]">
-          <div className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden">
-            <div
+        <div
+          data-testid="calendar-primary-controls"
+          className="flex flex-grow flex-row items-center gap-[10px] tablet:w-full tablet:flex-wrap"
+        >
+          <div
+            data-testid="calendar-list-pagination"
+            className="border h-[42px] border-newTableBorder bg-newTableBorder gap-[1px] flex items-center rounded-[8px] overflow-hidden tablet:w-full"
+          >
+            <button
+              type="button"
+              aria-label={t('previous_page', 'Previous page')}
+              disabled={calendar.listPage <= 0}
               onClick={previousPage}
               className={clsx(
                 'text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center',
@@ -376,13 +429,17 @@ export const Filters = () => {
                   strokeLinejoin="round"
                 />
               </svg>
-            </div>
-            <div className="min-w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center">
+            </button>
+            <div className="w-[200px] text-center bg-newBgColorInner h-full flex items-center justify-center tablet:w-auto tablet:min-w-0 tablet:flex-1">
               <div className="py-[3px] px-[9px] rounded-[5px] transition-all text-[14px]">
-                {t('page', 'Page')} {calendar.listPage + 1} {t('of', 'of')} {Math.max(1, calendar.listTotalPages)}
+                {t('page', 'Page')} {calendar.listPage + 1} {t('of', 'of')}{' '}
+                {Math.max(1, calendar.listTotalPages)}
               </div>
             </div>
-            <div
+            <button
+              type="button"
+              aria-label={t('next_page', 'Next page')}
+              disabled={calendar.listPage >= calendar.listTotalPages - 1}
               onClick={nextPage}
               className={clsx(
                 'text-textColor rtl:rotate-180 px-[9px] bg-newBgColorInner h-full flex items-center justify-center',
@@ -406,110 +463,146 @@ export const Filters = () => {
                   strokeLinejoin="round"
                 />
               </svg>
-            </div>
+            </button>
           </div>
-          <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]">
+          <div
+            data-testid="calendar-list-status"
+            className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500] tablet:grid tablet:w-full tablet:grid-cols-2"
+          >
             {listStateOptions.map((option) => (
-              <div
+              <button
+                type="button"
                 key={option.value}
+                aria-pressed={calendar.listState === option.value}
                 onClick={setListStateFilter(option.value)}
                 className={clsx(
-                  'pt-[6px] pb-[5px] cursor-pointer min-w-[80px] px-[12px] text-center rounded-[6px]',
+                  'pt-[6px] pb-[5px] cursor-pointer w-[80px] px-[12px] text-center rounded-[6px] tablet:w-auto',
                   calendar.listState === option.value &&
                     'text-textItemFocused bg-boxFocused'
                 )}
               >
                 {option.label}
-              </div>
+              </button>
             ))}
           </div>
           <div className="flex-1" />
         </div>
       )}
-      <SelectCustomer
-        customer={calendar.customer as string}
-        onChange={(customer: string) => setCustomer(customer)}
-        integrations={calendar.integrations}
-      />
-      {!isListView && (
-        <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]">
-          <div
-            className={clsx(
-              'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]',
-              calendar.display === 'day' && 'text-textItemFocused bg-boxFocused'
-            )}
-            onClick={setDay}
-          >
-            {t('day', 'Day')}
+      <div
+        data-testid="calendar-picker-controls"
+        className="flex items-start gap-[8px] tablet:w-full tablet:flex-wrap"
+      >
+        <IntegrationFilter
+          integrations={calendar.integrations}
+          selectedIds={calendar.integrationIds}
+          customerId={calendar.customer}
+          onChange={setIntegrations}
+        />
+        <SelectCustomer
+          customer={calendar.customer}
+          onChange={setCustomer}
+          integrations={calendar.integrations}
+        />
+      </div>
+      <div
+        data-testid="calendar-view-controls"
+        className="flex items-start gap-[8px] tablet:w-full tablet:flex-wrap"
+      >
+        {!isListView && (
+          <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500] tablet:grid tablet:w-full tablet:grid-cols-3">
+            <button
+              type="button"
+              aria-pressed={calendar.display === 'day'}
+              className={clsx(
+                'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px] tablet:w-auto',
+                calendar.display === 'day' &&
+                  'text-textItemFocused bg-boxFocused'
+              )}
+              onClick={setDay}
+            >
+              {t('day', 'Day')}
+            </button>
+            <button
+              type="button"
+              aria-pressed={calendar.display === 'week'}
+              className={clsx(
+                'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px] tablet:w-auto',
+                calendar.display === 'week' &&
+                  'text-textItemFocused bg-boxFocused'
+              )}
+              onClick={setWeek}
+            >
+              {t('week', 'Week')}
+            </button>
+            <button
+              type="button"
+              aria-pressed={calendar.display === 'month'}
+              className={clsx(
+                'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px] tablet:w-auto',
+                calendar.display === 'month' &&
+                  'text-textItemFocused bg-boxFocused'
+              )}
+              onClick={setMonth}
+            >
+              {t('month', 'Month')}
+            </button>
           </div>
-          <div
+        )}
+        <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]">
+          <button
+            type="button"
+            aria-label={t('calendar_view', 'Calendar view')}
+            aria-pressed={!isListView}
+            onClick={setCalendarView}
             className={clsx(
-              'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]',
-              calendar.display === 'week' && 'text-textItemFocused bg-boxFocused'
+              'pt-[6px] pb-[5px] cursor-pointer flex justify-center items-center w-[34px] text-center rounded-[6px]',
+              !isListView && 'text-textItemFocused bg-boxFocused'
             )}
-            onClick={setWeek}
           >
-            {t('week', 'Week')}
-          </div>
-          <div
+            {/*calendar*/}
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="17"
+              height="19"
+              viewBox="0 0 17 19"
+              fill="none"
+            >
+              <path
+                d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z"
+                stroke="currentColor"
+                strokeWidth="1.5"
+                strokeLinecap="round"
+                strokeLinejoin="round"
+              />
+            </svg>
+          </button>
+          <button
+            type="button"
+            aria-label={t('list_view', 'List view')}
+            aria-pressed={isListView}
+            onClick={setList}
             className={clsx(
-              'pt-[6px] pb-[5px] cursor-pointer w-[74px] text-center rounded-[6px]',
-              calendar.display === 'month' && 'text-textItemFocused bg-boxFocused'
+              'pt-[6px] pb-[5px] flex justify-center items-center cursor-pointer w-[34px] text-center rounded-[6px]',
+              isListView && 'text-textItemFocused bg-boxFocused'
             )}
-            onClick={setMonth}
-          >
-            {t('month', 'Month')}
-          </div>
-        </div>
-      )}
-      <div className="flex flex-row p-[4px] border border-newTableBorder rounded-[8px] text-[14px] font-[500]">
-        <div
-          onClick={setCalendarView}
-          className={clsx(
-            'pt-[6px] pb-[5px] cursor-pointer flex justify-center items-center w-[34px] text-center rounded-[6px]',
-            !isListView && 'text-textItemFocused bg-boxFocused'
-          )}
-        >
-          {/*calendar*/}
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="17"
-            height="19"
-            viewBox="0 0 17 19"
-            fill="none"
           >
-            <path
-              d="M15.75 7.41667H0.75M11.5833 0.75V4.08333M4.91667 0.75V4.08333M4.75 17.4167H11.75C13.1501 17.4167 13.8502 17.4167 14.385 17.1442C14.8554 16.9045 15.2378 16.522 15.4775 16.0516C15.75 15.5169 15.75 14.8168 15.75 13.4167V6.41667C15.75 5.01654 15.75 4.31647 15.4775 3.78169C15.2378 3.31129 14.8554 2.92883 14.385 2.68915C13.8502 2.41667 13.1501 2.41667 11.75 2.41667H4.75C3.34987 2.41667 2.6498 2.41667 2.11502 2.68915C1.64462 2.92883 1.26217 3.31129 1.02248 3.78169C0.75 4.31647 0.75 5.01654 0.75 6.41667V13.4167C0.75 14.8168 0.75 15.5169 1.02248 16.0516C1.26217 16.522 1.64462 16.9045 2.11502 17.1442C2.6498 17.4167 3.34987 17.4167 4.75 17.4167Z"
-              stroke="currentColor"
-              strokeWidth="1.5"
-              strokeLinecap="round"
-              strokeLinejoin="round"
-            />
-          </svg>
-        </div>
-        <div
-          onClick={setList}
-          className={clsx(
-            'pt-[6px] pb-[5px] flex justify-center items-center cursor-pointer w-[34px] text-center rounded-[6px]',
-            isListView && 'text-textItemFocused bg-boxFocused'
-          )}
-        >
-          {/*list*/}
-          <svg
-            xmlns="http://www.w3.org/2000/svg"
-            width="20"
-            height="20"
-            viewBox="0 0 20 20"
-            fill="none"
-          >
-            <path
-              d="M17.5 10L7.5 10M17.5 5.00002L7.5 5.00002M17.5 15L7.5 15M4.16667 10C4.16667 10.4603 3.79357 10.8334 3.33333 10.8334C2.8731 10.8334 2.5 10.4603 2.5 10C2.5 9.53978 2.8731 9.16669 3.33333 9.16669C3.79357 9.16669 4.16667 9.53978 4.16667 10ZM4.16667 5.00002C4.16667 5.46026 3.79357 5.83335 3.33333 5.83335C2.8731 5.83335 2.5 5.46026 2.5 5.00002C2.5 4.53978 2.8731 4.16669 3.33333 4.16669C3.79357 4.16669 4.16667 4.53978 4.16667 5.00002ZM4.16667 15C4.16667 15.4603 3.79357 15.8334 3.33333 15.8334C2.8731 15.8334 2.5 15.4603 2.5 15C2.5 14.5398 2.8731 14.1667 3.33333 14.1667C3.79357 14.1667 4.16667 14.5398 4.16667 15Z"
-              stroke="currentColor"
-              strokeWidth="1.5"
-              strokeLinecap="round"
-              strokeLinejoin="round"
-            />
-          </svg>
+            {/*list*/}
+            <svg
+              xmlns="http://www.w3.org/2000/svg"
+              width="20"
+              height="20"
+              viewBox="0 0 20 20"
+              fill="none"
+            >
+              <path
+                d="M17.5 10L7.5 10M17.5 5.00002L7.5 5.00002M17.5 15L7.5 15M4.16667 10C4.16667 10.4603 3.79357 10.8334 3.33333 10.8334C2.8731 10.8334 2.5 10.4603 2.5 10C2.5 9.53978 2.8731 9.16669 3.33333 9.16669C3.79357 9.16669 4.16667 9.53978 4.16667 10ZM4.16667 5.00002C4.16667 5.46026 3.79357 5.83335 3.33333 5.83335C2.8731 5.83335 2.5 5.46026 2.5 5.00002C2.5 4.53978 2.8731 4.16669 3.33333 4.16669C3.79357 4.16669 4.16667 4.53978 4.16667 5.00002ZM4.16667 15C4.16667 15.4603 3.79357 15.8334 3.33333 15.8334C2.8731 15.8334 2.5 15.4603 2.5 15C2.5 14.5398 2.8731 14.1667 3.33333 14.1667C3.79357 14.1667 4.16667 14.5398 4.16667 15Z"
+                stroke="currentColor"
+                strokeWidth="1.5"
+                strokeLinecap="round"
+                strokeLinejoin="round"
+              />
+            </svg>
+          </button>
         </div>
       </div>
     </div>
diff --git a/apps/frontend/src/components/launches/generator/generator.tsx b/apps/frontend/src/components/launches/generator/generator.tsx
index 0bb0719..70d7e4b 100644
--- a/apps/frontend/src/components/launches/generator/generator.tsx
+++ b/apps/frontend/src/components/launches/generator/generator.tsx
@@ -314,7 +314,7 @@ export const GeneratorComponent = () => {
       },
       size: 'xl',
       children: (
-        <CalendarWeekProvider {...all}>
+        <CalendarWeekProvider {...all} integrationsReady={true}>
           <GeneratorPopup />
         </CalendarWeekProvider>
       ),
diff --git a/apps/frontend/src/components/launches/helpers/use.integration.list.tsx b/apps/frontend/src/components/launches/helpers/use.integration.list.tsx
index 4e3d2a5..6084734 100644
--- a/apps/frontend/src/components/launches/helpers/use.integration.list.tsx
+++ b/apps/frontend/src/components/launches/helpers/use.integration.list.tsx
@@ -7,17 +7,35 @@ import useSWR from 'swr';
 export const useIntegrationList = () => {
   const fetch = useFetch();
 
-  const load = useCallback(async (path: string) => {
-    return (await (await fetch(path)).json()).integrations;
-  }, []);
+  const load = useCallback(
+    async (path: string) => {
+      const response = await fetch(path);
+      if (!response.ok) throw new Error('Integration list request failed');
+      try {
+        const payload = await response.json();
+        if (Array.isArray(payload?.integrations)) {
+          return payload.integrations;
+        }
+      } catch {
+        // Normalize parse failures below without exposing response details.
+      }
+      throw new Error('Integration list request failed');
+    },
+    [fetch]
+  );
 
-  return useSWR('/integrations/list', load, {
+  const result = useSWR('/integrations/list', load, {
     revalidateOnFocus: false,
     revalidateOnReconnect: false,
     revalidateIfStale: false,
     revalidateOnMount: true,
     refreshWhenHidden: false,
     refreshWhenOffline: false,
-    fallbackData: [],
   });
-};
\ No newline at end of file
+
+  return {
+    ...result,
+    integrationsReady: result.data !== undefined,
+    initialLoadError: !!result.error && result.data === undefined,
+  };
+};
diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx
index 8544a3e..b47a2e0 100644
--- a/apps/frontend/src/components/launches/launches.component.tsx
+++ b/apps/frontend/src/components/launches/launches.component.tsx
@@ -26,6 +26,7 @@ import { useT } from '@gitroom/react/translation/get.transation.service.client';
 import { useIntegrationList } from '@gitroom/frontend/components/launches/helpers/use.integration.list';
 import useCookie from 'react-use-cookie';
 import { Onboarding } from '@gitroom/frontend/components/onboarding/onboarding';
+import { IntegrationLoadError } from '@gitroom/frontend/components/launches/integration-load-error';
 
 export const SVGLine = () => {
   return (
@@ -361,7 +362,13 @@ export const LaunchesComponent = () => {
   const [reload, setReload] = useState(false);
   const [collapseMenu, setCollapseMenu] = useCookie('collapseMenu', '0');
   const [mode] = useCookie('mode', 'dark');
-  const { isLoading, data: integrations, mutate } = useIntegrationList();
+  const {
+    isLoading,
+    data: integrations,
+    mutate,
+    integrationsReady,
+    initialLoadError,
+  } = useIntegrationList();
 
   const totalNonDisabledChannels = useMemo(() => {
     return (
@@ -491,15 +498,21 @@ export const LaunchesComponent = () => {
       </div>
     );
   }
+  if (initialLoadError) {
+    return <IntegrationLoadError onRetry={() => mutate()} />;
+  }
 
   // @ts-ignore
   return (
     <DNDProvider>
       <Onboarding />
-      <CalendarWeekProvider integrations={sortedIntegrations}>
+      <CalendarWeekProvider
+        integrations={sortedIntegrations}
+        integrationsReady={integrationsReady}
+      >
         <div
           className={clsx(
-            'flex relative flex-col',
+            'flex relative flex-col mobile:hidden',
             collapseMenu === '1' ? 'group sidebar w-[100px]' : 'w-[260px]'
           )}
         >
@@ -592,9 +605,17 @@ export const LaunchesComponent = () => {
             </div>
           </div>
         </div>
-        <div className="bg-newBgColorInner flex-1 flex-col flex p-[20px] gap-[12px]">
+        <div className="bg-newBgColorInner min-w-0 flex-1 flex-col flex p-[20px] gap-[12px]">
+          {sortedIntegrations?.length > 0 && (
+            <div
+              data-testid="calendar-mobile-create-post"
+              className="hidden mobile:flex"
+            >
+              <NewPost />
+            </div>
+          )}
           <Filters />
-          <div className="flex-1 flex">
+          <div className="min-w-0 flex-1 flex">
             <Calendar />
           </div>
         </div>
diff --git a/apps/frontend/src/components/launches/select.customer.tsx b/apps/frontend/src/components/launches/select.customer.tsx
index 2b42acc..1afb23d 100644
--- a/apps/frontend/src/components/launches/select.customer.tsx
+++ b/apps/frontend/src/components/launches/select.customer.tsx
@@ -1,22 +1,42 @@
 'use client';
 
 import { uniqBy } from 'lodash';
-import React, { FC, useCallback, useMemo, useRef, useState } from 'react';
-import { Integrations } from '@gitroom/frontend/components/launches/calendar.context';
+import React, {
+  type CSSProperties,
+  FC,
+  type KeyboardEvent,
+  useEffect,
+  useMemo,
+  useRef,
+  useState,
+} from 'react';
+import type { CalendarIntegration } from '@gitroom/frontend/components/launches/calendar.types';
+import { getBoundedPopupStyle } from '@gitroom/frontend/components/launches/bounded-popup';
 import { useT } from '@gitroom/react/translation/get.transation.service.client';
 import clsx from 'clsx';
 import { useClickOutside } from '@mantine/hooks';
 import { useToaster } from '@gitroom/react/toaster/toaster';
 import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
 import { useShallow } from 'zustand/react/shallow';
-import { UserIcon, DropdownArrowIcon } from '@gitroom/frontend/components/ui/icons';
+import {
+  UserIcon,
+  DropdownArrowIcon,
+} from '@gitroom/frontend/components/ui/icons';
 
-export const SelectCustomer: FC<{
-  onChange: (value: string) => void;
-  integrations: Integrations[];
-  customer?: string;
-}> = (props) => {
-  const { onChange, integrations, customer: currentCustomer } = props;
+interface SelectCustomerProps {
+  onChange: (value: string | null) => void;
+  integrations: readonly CalendarIntegration[];
+  customer?: string | null;
+}
+
+const focusClasses =
+  'focus-visible:ring-2 focus-visible:ring-btnPrimary focus-visible:ring-offset-2 focus-visible:ring-offset-newBgColorInner';
+
+export const SelectCustomer: FC<SelectCustomerProps> = ({
+  onChange,
+  integrations,
+  customer = null,
+}) => {
   const { setCurrent } = useLaunchStore(
     useShallow((state) => ({
       setCurrent: state.setCurrent,
@@ -24,79 +44,234 @@ export const SelectCustomer: FC<{
   );
   const toaster = useToaster();
   const t = useT();
-  const [customer, setCustomer] = useState(currentCustomer || '');
-  const [pos, setPos] = useState<any>({});
   const [open, setOpen] = useState(false);
+  const [activeCustomerId, setActiveCustomerId] = useState<string | null>(null);
+  const [popupStyle, setPopupStyle] = useState<CSSProperties>();
+  const triggerRef = useRef<HTMLButtonElement>(null);
+  const itemRefs = useRef(new Map<string, HTMLButtonElement>());
   const ref = useClickOutside(() => {
     if (open) {
       setOpen(false);
     }
   });
 
-  const openClose = useCallback(() => {
+  const customers = useMemo(
+    () =>
+      uniqBy(integrations, (integration) => integration.customer?.id).filter(
+        (integration) => integration.customer?.id
+      ),
+    [integrations]
+  );
+  const closeAndFocusTrigger = () => {
+    setOpen(false);
+    triggerRef.current?.focus();
+  };
+  const resolvedCustomerLabel = (integration: CalendarIntegration) =>
+    integration.customer?.name?.trim() || integration.customer!.id!;
+  const customerLabelCounts = customers.reduce<Record<string, number>>(
+    (counts, integration) => {
+      const label = resolvedCustomerLabel(integration);
+      counts[label] = (counts[label] || 0) + 1;
+      return counts;
+    },
+    {}
+  );
+  const customerMenuLabel = (integration: CalendarIntegration) => {
+    const label = resolvedCustomerLabel(integration);
+    return customerLabelCounts[label] > 1
+      ? `${label} · ${integration.customer!.id}`
+      : label;
+  };
+  const items = [
+    { id: null, label: t('calendar_all_customers', 'All customers') },
+    ...customers.map((integration) => ({
+      id: integration.customer!.id!,
+      label: customerMenuLabel(integration),
+    })),
+  ];
+  const effectiveActiveCustomerId = items.some(
+    (item) => item.id === activeCustomerId
+  )
+    ? activeCustomerId
+    : items.some((item) => item.id === customer)
+    ? customer
+    : null;
+  const activeIndex = Math.max(
+    0,
+    items.findIndex((item) => item.id === effectiveActiveCustomerId)
+  );
+  const triggerLabel =
+    items.find((item) => item.id === customer)?.label ||
+    customer ||
+    items[0].label;
+  const selectCustomer = (value: string | null) => {
+    toaster.show(
+      t('customer_socials_selected', 'Customer socials selected'),
+      'success'
+    );
+    onChange(value);
+    setCurrent('global');
+    closeAndFocusTrigger();
+  };
+  const focusItem = (index: number) => {
+    const nextIndex = (index + items.length) % items.length;
+    const nextId = items[nextIndex].id;
+    setActiveCustomerId(nextId);
+    itemRefs.current.get(nextId || '')?.focus();
+  };
+  const handleMenuKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
+    if (event.key === 'Escape') {
+      event.preventDefault();
+      closeAndFocusTrigger();
+      return;
+    }
+    if (event.key === 'ArrowDown') {
+      event.preventDefault();
+      focusItem(activeIndex + 1);
+    } else if (event.key === 'ArrowUp') {
+      event.preventDefault();
+      focusItem(activeIndex - 1);
+    } else if (event.key === 'Home') {
+      event.preventDefault();
+      focusItem(0);
+    } else if (event.key === 'End') {
+      event.preventDefault();
+      focusItem(items.length - 1);
+    } else if (event.key === 'Enter' || event.key === ' ') {
+      event.preventDefault();
+      const item = items[activeIndex];
+      if (item) selectCustomer(item.id);
+    }
+  };
+  useEffect(() => {
+    if (!open) return;
+    itemRefs.current.get(effectiveActiveCustomerId || '')?.focus();
+    const updatePosition = () => {
+      const trigger = triggerRef.current;
+      if (trigger) {
+        setPopupStyle(
+          getBoundedPopupStyle(trigger.getBoundingClientRect(), 250, 360)
+        );
+      }
+    };
+    window.addEventListener('resize', updatePosition);
+    window.addEventListener('scroll', updatePosition, true);
+    return () => {
+      window.removeEventListener('resize', updatePosition);
+      window.removeEventListener('scroll', updatePosition, true);
+    };
+  }, [effectiveActiveCustomerId, open]);
+  const toggleMenu = () => {
     if (open) {
       setOpen(false);
       return;
     }
-
-    const { x, y, width, height } = ref.current?.getBoundingClientRect();
-    setPos({ top: y + height, left: x });
+    const trigger = triggerRef.current;
+    if (!trigger) return;
+    setPopupStyle(
+      getBoundedPopupStyle(trigger.getBoundingClientRect(), 250, 360)
+    );
+    setActiveCustomerId(
+      items.some((item) => item.id === customer) ? customer : null
+    );
     setOpen(true);
-  }, [open]);
-
-  const totalCustomers = useMemo(() => {
-    return uniqBy(integrations, (i) => i?.customer?.id).length;
-  }, [integrations]);
-  if (totalCustomers <= 1) {
+  };
+  if (!customers.length && customer === null) {
     return null;
   }
 
   return (
-    <div className="relative select-none z-[500]" ref={ref}>
-      <div
+    <div
+      className="relative z-[500] select-none"
+      ref={ref}
+      onKeyDown={(event) => {
+        if (event.key === 'Escape') closeAndFocusTrigger();
+      }}
+    >
+      <button
+        ref={triggerRef}
+        type="button"
+        aria-label={triggerLabel}
+        aria-expanded={open}
+        aria-haspopup="menu"
         data-tooltip-id="tooltip"
-        data-tooltip-content={t('select_customer_tooltip', 'Select Customer')}
-        onClick={openClose}
+        data-tooltip-content={triggerLabel}
+        onClick={toggleMenu}
         className={clsx(
-          'relative z-[20] cursor-pointer h-[42px] rounded-[8px] pl-[16px] pr-[12px] gap-[8px] border flex items-center',
-          open ? 'border-[#612BD3]' : 'border-newColColor'
+          `relative z-[20] flex h-[42px] cursor-pointer items-center gap-[8px] rounded-[8px] border pl-[16px] pr-[12px] ${focusClasses}`,
+          open ? 'border-btnPrimary' : 'border-newColColor'
         )}
       >
         <div>
           <UserIcon />
         </div>
+        <span className="max-w-[150px] truncate text-[14px] font-[500]">
+          {triggerLabel}
+        </span>
         <div>
           <DropdownArrowIcon rotated={open} />
         </div>
-      </div>
+      </button>
       {open && (
         <div
-          style={pos}
-          className="flex flex-col fixed pt-[12px] bg-newBgColorInner menu-shadow min-w-[250px]"
+          role="menu"
+          onKeyDown={handleMenuKeyDown}
+          style={popupStyle}
+          className="z-[510] flex flex-col overflow-y-auto rounded-[8px] bg-newBgColorInner p-[8px] shadow-menu"
         >
           <div className="text-[14px] font-[600] px-[12px] mb-[5px]">
             {t('customers', 'Customers')}
           </div>
-          {uniqBy(integrations, (u) => u?.customer?.name)
-            .filter((f) => f.customer?.name)
-            .map((p) => (
-              <div
-                onClick={() => {
-                  toaster.show(
-                    t('customer_socials_selected', 'Customer socials selected'),
-                    'success'
-                  );
-                  setCustomer(p.customer?.id);
-                  onChange(p.customer?.id);
-                  setOpen(false);
-                  setCurrent('global')
-                }}
-                key={p.customer?.id}
-                className="p-[12px] hover:bg-newBgColor text-[14px] font-[500] h-[32px] flex items-center"
-              >
-                {p.customer?.name}
-              </div>
-            ))}
+          <button
+            ref={(element) => {
+              if (element) itemRefs.current.set('', element);
+              else itemRefs.current.delete('');
+            }}
+            type="button"
+            role="menuitem"
+            aria-current={customer === null ? 'true' : undefined}
+            className={`flex h-[36px] items-center rounded-[6px] px-[12px] text-[14px] font-[500] hover:bg-newBgColor ${focusClasses}`}
+            onClick={() => selectCustomer(null)}
+            tabIndex={effectiveActiveCustomerId === null ? 0 : -1}
+            onFocus={() => setActiveCustomerId(null)}
+            onKeyDown={(event) => {
+              if (event.key !== 'Enter' && event.key !== ' ') return;
+              event.stopPropagation();
+              event.preventDefault();
+              selectCustomer(null);
+            }}
+          >
+            {t('calendar_all_customers', 'All customers')}
+          </button>
+          {customers.map((integration) => (
+            <button
+              ref={(element) => {
+                const id = integration.customer!.id!;
+                if (element) itemRefs.current.set(id, element);
+                else itemRefs.current.delete(id);
+              }}
+              type="button"
+              role="menuitem"
+              aria-current={
+                customer === integration.customer?.id ? 'true' : undefined
+              }
+              onClick={() => selectCustomer(integration.customer!.id!)}
+              tabIndex={
+                effectiveActiveCustomerId === integration.customer!.id! ? 0 : -1
+              }
+              onFocus={() => setActiveCustomerId(integration.customer!.id!)}
+              onKeyDown={(event) => {
+                if (event.key !== 'Enter' && event.key !== ' ') return;
+                event.stopPropagation();
+                event.preventDefault();
+                selectCustomer(integration.customer!.id!);
+              }}
+              key={integration.customer?.id}
+              className={`flex h-[36px] items-center rounded-[6px] px-[12px] text-[14px] font-[500] hover:bg-newBgColor ${focusClasses}`}
+            >
+              {customerMenuLabel(integration)}
+            </button>
+          ))}
         </div>
       )}
     </div>
diff --git a/apps/frontend/tailwind.config.cjs b/apps/frontend/tailwind.config.cjs
index 1aa72aa..85f548f 100644
--- a/apps/frontend/tailwind.config.cjs
+++ b/apps/frontend/tailwind.config.cjs
@@ -98,6 +98,16 @@ module.exports = {
         newSettings: 'var(--new-settings)',
         menuDots: 'var(--new-menu-dots)',
         menuDotsHover: 'var(--new-menu-hover)',
+        statusError: 'var(--new-status-error)',
+        statusErrorSurface: 'var(--new-status-error-surface)',
+        statusWarning: 'var(--new-status-warning)',
+        statusWarningSurface: 'var(--new-status-warning-surface)',
+        statusInfo: 'var(--new-status-info)',
+        statusInfoSurface: 'var(--new-status-info-surface)',
+        statusSuccess: 'var(--new-status-success)',
+        statusSuccessSurface: 'var(--new-status-success-surface)',
+        statusNeutral: 'var(--new-status-neutral)',
+        statusNeutralSurface: 'var(--new-status-neutral-surface)',
         bigStrip: 'var(--new-big-strips)',
         popup: 'var(--popup-color)',
         bgLinkedin: 'var(--linkedin-bg)',
diff --git a/apps/orchestrator/src/activities/post.activity.ts b/apps/orchestrator/src/activities/post.activity.ts
index 781d789..4de407c 100644
--- a/apps/orchestrator/src/activities/post.activity.ts
+++ b/apps/orchestrator/src/activities/post.activity.ts
@@ -106,8 +106,13 @@ export class PostActivity {
   }
 
   @ActivityMethod()
-  async updatePost(id: string, postId: string, releaseURL: string) {
-    await this._postService.updatePost(id, postId, releaseURL);
+  async updatePost(
+    id: string,
+    postId: string,
+    releaseURL: string,
+    warning?: string
+  ) {
+    await this._postService.updatePost(id, postId, releaseURL, warning);
   }
 
   @ActivityMethod()
@@ -250,20 +255,24 @@ export class PostActivity {
       integration
     );
 
-    await this._temporalService.client
-      .getRawClient()
-      .workflow.start('streakWorkflow', {
-        args: [{ organizationId: integration.organizationId }],
-        workflowId: `streak_${integration.organizationId}`,
-        taskQueue: 'main',
-        workflowIdConflictPolicy: 'TERMINATE_EXISTING',
-        typedSearchAttributes: new TypedSearchAttributes([
-          {
-            key: organizationId,
-            value: integration.organizationId,
-          },
-        ]),
-      });
+    try {
+      await this._temporalService.client
+        .getRawClient()
+        .workflow.start('streakWorkflow', {
+          args: [{ organizationId: integration.organizationId }],
+          workflowId: `streak_${integration.organizationId}`,
+          taskQueue: 'main',
+          workflowIdConflictPolicy: 'TERMINATE_EXISTING',
+          typedSearchAttributes: new TypedSearchAttributes([
+            {
+              key: organizationId,
+              value: integration.organizationId,
+            },
+          ]),
+        });
+    } catch {
+      console.error('Failed to start streak workflow after publishing post.');
+    }
 
     return postNow;
   }
diff --git a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.5.ts b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.5.ts
index 1e2d53d..fad979b 100644
--- a/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.5.ts
+++ b/apps/orchestrator/src/workflows/post-workflows/post.workflow.v1.0.5.ts
@@ -17,16 +17,26 @@ import { TypedSearchAttributes } from '@temporalio/common';
 import { postId as postIdSearchParam } from '@gitroom/nestjs-libraries/temporal/temporal.search.attribute';
 import { summarizeWorkflowError } from '@gitroom/orchestrator/workflows/post-workflows/workflow.error';
 import { formatPostFailureNotification } from '@gitroom/nestjs-libraries/database/prisma/notifications/channel-error.notification';
+import {
+  plugAttemptCount,
+  publishActivityRetry,
+  refreshRetryAttemptCount,
+} from './post.retry';
+
+const supportActivityRetry = {
+  maximumAttempts: 3,
+  backoffCoefficient: 1,
+  initialInterval: '2 minutes' as const,
+};
 
-const proxyTaskQueue = (taskQueue: string) => {
+const proxyTaskQueue = (
+  taskQueue: string,
+  retry: typeof supportActivityRetry
+) => {
   return proxyActivities<PostActivity>({
     startToCloseTimeout: '10 minute',
     taskQueue,
-    retry: {
-      maximumAttempts: 3,
-      backoffCoefficient: 1,
-      initialInterval: '2 minutes',
-    },
+    retry,
   });
 };
 
@@ -49,7 +59,8 @@ const {
 
 const poke = defineSignal('poke');
 
-const iterate = Array.from({ length: 5 });
+const publishAttempts = Array.from({ length: refreshRetryAttemptCount });
+const plugAttempts = Array.from({ length: plugAttemptCount });
 
 export async function postWorkflowV105({
   taskQueue,
@@ -63,16 +74,18 @@ export async function postWorkflowV105({
   postNow?: boolean;
 }) {
   // Dynamic task queue, for concurrency
+  const { postSocial, postComment } = proxyTaskQueue(
+    taskQueue,
+    publishActivityRetry
+  );
   const {
-    postSocial,
-    postComment,
     getIntegrationById,
     refreshTokenWithCause,
     internalPlugs,
     globalPlugs,
     processInternalPlug,
     processPlug,
-  } = proxyTaskQueue(taskQueue);
+  } = proxyTaskQueue(taskQueue, supportActivityRetry);
 
   let poked = false;
   setHandler(poke, () => {
@@ -166,7 +179,7 @@ export async function postWorkflowV105({
   for (let i = 0; i < postsList.length; i++) {
     const before = postsResults.length;
     // this is a small trick to repeat an action in case of token refresh
-    for (const _ of iterate) {
+    for (let attempt = 0; attempt < publishAttempts.length; attempt++) {
       try {
         // first post the main post
         if (i === 0) {
@@ -198,7 +211,10 @@ export async function postWorkflowV105({
         await updatePost(
           postsList[i].id,
           postsResults[i].postId,
-          postsResults[i].releaseURL
+          postsResults[i].releaseURL,
+          typeof postsResults[i].warning === 'string'
+            ? postsResults[i].warning
+            : undefined
         );
 
         if (i === 0) {
@@ -219,43 +235,38 @@ export async function postWorkflowV105({
         // break the current while to move to the next post
         break;
       } catch (err) {
-        // if token refresh is needed, do it and repeat
         if (
+          attempt === 0 &&
           err instanceof ActivityFailure &&
           err.cause instanceof ApplicationFailure &&
           err.cause.type === 'refresh_token'
         ) {
           const refresh = await refreshTokenWithCause(
             post.integration,
-            err?.cause?.message || ''
+            err.cause.message || ''
           );
-          if (!refresh || !refresh.accessToken) {
-            await changeState(
-              postsList[0].id,
-              'ERROR',
-              summarizeWorkflowError(err),
-              postsList
-            );
-            return false;
+          if (refresh && refresh.accessToken) {
+            post.integration.token = refresh.accessToken;
+            continue;
           }
-
-          post.integration.token = refresh.accessToken;
-          continue;
         }
 
-        // for other errors, change state and inform the user if needed
-        await changeState(
-          postsList[0].id,
-          'ERROR',
-          summarizeWorkflowError(err),
-          postsList
-        );
+        // The parent is already published when a comment fails. Never make a
+        // manual retry capable of publishing the parent a second time.
+        if (i === 0) {
+          await changeState(
+            postsList[0].id,
+            'ERROR',
+            summarizeWorkflowError(err),
+            postsList
+          );
+        }
 
-        // specific case for bad body errors
         if (
-          err instanceof ActivityFailure &&
-          err.cause instanceof ApplicationFailure &&
-          err.cause.type === 'bad_body'
+          i !== 0 ||
+          (err instanceof ActivityFailure &&
+            err.cause instanceof ApplicationFailure &&
+            err.cause.type === 'bad_body')
         ) {
           await inAppNotification(
             post.organizationId,
@@ -265,15 +276,20 @@ export async function postWorkflowV105({
             formatPostFailureNotification({
               providerIdentifier: post.integration.providerIdentifier,
               integrationName: post.integration.name,
-              errorMessage: err.cause.message,
+              errorMessage:
+                err instanceof ActivityFailure &&
+                err.cause instanceof ApplicationFailure
+                  ? err.cause.message
+                  : summarizeWorkflowError(err).message,
               isComment: i !== 0,
             }),
             true,
             false,
             'fail'
           );
-          return false;
         }
+
+        return false;
       }
     }
 
@@ -339,7 +355,7 @@ export async function postWorkflowV105({
 
     // process internal plug
     if (todo.type === 'internal-plug') {
-      for (const _ of iterate) {
+      for (const _ of plugAttempts) {
         try {
           await processInternalPlug({ ...todo, post: postsResults[0].postId });
         } catch (err) {
@@ -350,12 +366,11 @@ export async function postWorkflowV105({
           ) {
             const refresh = await refreshTokenWithCause(
               await getIntegrationById(organizationId, todo.integration),
-              err?.cause?.message || ''
+              err.cause.message || ''
             );
             if (!refresh || !refresh.accessToken) {
               break;
             }
-
             continue;
           }
 
@@ -375,7 +390,7 @@ export async function postWorkflowV105({
 
     // process global plug
     if (todo.type === 'global') {
-      for (const _ of iterate) {
+      for (const _ of plugAttempts) {
         try {
           const process = await processPlug({
             ...todo,
@@ -404,12 +419,11 @@ export async function postWorkflowV105({
           ) {
             const refresh = await refreshTokenWithCause(
               post.integration,
-              err?.cause?.message || ''
+              err.cause.message || ''
             );
             if (!refresh || !refresh.accessToken) {
               break;
             }
-
             continue;
           }
 
diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts
index 4cd90cc..c221349 100644
--- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.repository.ts
@@ -157,19 +157,16 @@ export class PostsRepository {
             ],
           },
         ],
+        ...(query.integrationIds?.length
+          ? { integrationId: { in: query.integrationIds } }
+          : {}),
         integration: {
           deletedAt: null,
           organizationId: orgId,
+          ...(query.customer ? { customerId: query.customer } : {}),
         },
         deletedAt: null,
         parentPostId: null,
-        ...(query.customer
-          ? {
-              integration: {
-                customerId: query.customer,
-              },
-            }
-          : {}),
       },
       select: {
         id: true,
@@ -177,6 +174,7 @@ export class PostsRepository {
         publishDate: true,
         releaseURL: true,
         releaseId: true,
+        error: true,
         state: true,
         intervalInDays: true,
         group: true,
@@ -199,15 +197,18 @@ export class PostsRepository {
 
     return list.reduce((all, post) => {
       if (!post.intervalInDays) {
-        return [...all, post];
+        const { error, ...safePost } = post;
+        return [...all, { ...safePost, message: error }];
       }
 
       const addMorePosts = [];
       let startingDate = dayjs.utc(post.publishDate);
       while (dayjs.utc(endDate).isSameOrAfter(startingDate)) {
         if (dayjs(startingDate).isSameOrAfter(dayjs.utc(post.publishDate))) {
+          const { error, ...safePost } = post;
           addMorePosts.push({
-            ...post,
+            ...safePost,
+            message: error,
             publishDate: startingDate.toDate(),
             actualDate: post.publishDate,
           });
@@ -256,12 +257,15 @@ export class PostsRepository {
       ],
       ...stateAndDate,
       publishDate: { gte: dayjs.utc().toDate() },
+      ...(query.integrationIds?.length
+        ? { integrationId: { in: query.integrationIds } }
+        : {}),
       deletedAt: null as Date | null,
       parentPostId: null as string | null,
       intervalInDays: null as number | null,
 
       integration: {
-        deletedAt: null as any,
+        deletedAt: null as Date | null,
         organizationId: orgId,
         ...(query.customer
           ? {
@@ -285,6 +289,7 @@ export class PostsRepository {
           publishDate: true,
           releaseURL: true,
           releaseId: true,
+          error: true,
           state: true,
           intervalInDays: true,
           group: true,
@@ -308,7 +313,7 @@ export class PostsRepository {
     ]);
 
     return {
-      posts,
+      posts: posts.map(({ error, ...post }) => ({ ...post, message: error })),
       total,
       page,
       limit,
@@ -385,7 +390,12 @@ export class PostsRepository {
     });
   }
 
-  updatePost(id: string, postId: string, releaseURL: string) {
+  updatePost(
+    id: string,
+    postId: string,
+    releaseURL: string,
+    warning?: string
+  ) {
     return this._post.model.post.update({
       where: {
         id,
@@ -394,6 +404,7 @@ export class PostsRepository {
         state: 'PUBLISHED',
         releaseURL,
         releaseId: postId,
+        error: warning ?? null,
       },
     });
   }
diff --git a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts
index a3dfdb5..91a7f86 100644
--- a/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts
+++ b/libraries/nestjs-libraries/src/database/prisma/posts/posts.service.ts
@@ -77,8 +77,13 @@ export class PostsService {
     return this._postRepository.searchForMissingThreeHoursPosts();
   }
 
-  updatePost(id: string, postId: string, releaseURL: string) {
-    return this._postRepository.updatePost(id, postId, releaseURL);
+  updatePost(
+    id: string,
+    postId: string,
+    releaseURL: string,
+    warning?: string
+  ) {
+    return this._postRepository.updatePost(id, postId, releaseURL, warning);
   }
 
   async getMissingContent(
diff --git a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts
index 9480781..af60f2f 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/get.posts.dto.ts
@@ -1,8 +1,13 @@
+import { Transform } from 'class-transformer';
 import {
+  ArrayMaxSize,
+  IsArray,
+  IsDateString,
   IsOptional,
   IsString,
-  IsDateString,
+  Matches,
 } from 'class-validator';
+import { parseIntegrationIdsQuery } from './integration-ids.query';
 
 export class GetPostsDto {
   @IsDateString()
@@ -14,4 +19,12 @@ export class GetPostsDto {
   @IsOptional()
   @IsString()
   customer: string;
+
+  @IsOptional()
+  @Transform(({ value }) => parseIntegrationIdsQuery(value))
+  @IsArray()
+  @ArrayMaxSize(100)
+  @IsString({ each: true })
+  @Matches(/^[A-Za-z0-9_-]{1,128}$/, { each: true })
+  integrationIds?: string[];
 }
diff --git a/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts b/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts
index 70e764e..59a347b 100644
--- a/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts
+++ b/libraries/nestjs-libraries/src/dtos/posts/get.posts.list.dto.ts
@@ -1,12 +1,16 @@
 import {
+  ArrayMaxSize,
+  IsArray,
+  IsIn,
+  IsNumber,
   IsOptional,
   IsString,
-  IsNumber,
-  Min,
   Max,
-  IsIn,
+  Matches,
+  Min,
 } from 'class-validator';
 import { Transform } from 'class-transformer';
+import { parseIntegrationIdsQuery } from './integration-ids.query';
 
 export type PostListStateFilter = 'all' | 'scheduled' | 'draft' | 'published';
 
@@ -28,6 +32,14 @@ export class GetPostsListDto {
   @IsString()
   customer?: string;
 
+  @IsOptional()
+  @Transform(({ value }) => parseIntegrationIdsQuery(value))
+  @IsArray()
+  @ArrayMaxSize(100)
+  @IsString({ each: true })
+  @Matches(/^[A-Za-z0-9_-]{1,128}$/, { each: true })
+  integrationIds?: string[];
+
   @IsOptional()
   @IsIn(['all', 'scheduled', 'draft', 'published'])
   state?: PostListStateFilter = 'all';
diff --git a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts
index f8b706d..f976714 100644
--- a/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts
+++ b/libraries/nestjs-libraries/src/integrations/social/social.integrations.interface.ts
@@ -104,6 +104,7 @@ export type PostResponse = {
   postId: string; // The ID of the scheduled post returned by the platform
   releaseURL: string; // The URL of the post on the platform
   status: string; // Status of the operation or initial post status
+  warning?: string;
 };
 
 export type PostDetails<T = any> = {
diff --git a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts
index 59bb2bb..cb994f6 100644
--- a/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts
+++ b/libraries/nestjs-libraries/src/integrations/social/youtube.provider.ts
@@ -105,18 +105,35 @@ export async function handleYoutubeThumbnail(
   setThumbnail: () => Promise<unknown>,
   postId: string,
   videoId: string
-): Promise<void> {
+): Promise<string | undefined> {
   try {
     await setThumbnail();
+    return undefined;
   } catch (error) {
-    if (!isUnverifiedYoutubeThumbnailError(error)) {
-      throw error;
-    }
-
-    console.warn(
-      'YouTube video uploaded without a custom thumbnail because the channel is not verified.',
-      { postId, videoId }
-    );
+    const diagnostic = (() => {
+      if (error instanceof Error) {
+        return error.message;
+      }
+      try {
+        return JSON.stringify(error);
+      } catch {
+        return '';
+      }
+    })();
+    const warning = isUnverifiedYoutubeThumbnailError(error)
+      ? 'YouTube video uploaded without a custom thumbnail because the channel is not verified.'
+      : diagnostic.includes('imageFormatUnsupported')
+      ? 'We have uploaded your video but the thumbnail format is not supported, please use JPEG or PNG.'
+      : diagnostic.includes('imageTooTall')
+      ? 'We have uploaded your video but the thumbnail image is too tall.'
+      : diagnostic.includes('imageTooWide')
+      ? 'We have uploaded your video but the thumbnail image is too wide.'
+      : diagnostic.includes('failedPrecondition')
+      ? 'We have uploaded your video but we could not set the thumbnail. Thumbnail size is too large.'
+      : 'Video đã đăng nhưng không thể đặt thumbnail tùy chỉnh.';
+
+    console.warn(warning, { postId, videoId });
+    return warning;
   }
 }
 
@@ -542,25 +559,35 @@ export class YoutubeProvider extends SocialAbstract implements SocialProvider {
     }
 
     if (settings?.thumbnail?.path) {
-      await handleYoutubeThumbnail(
+      const thumbnail = (
+        await axios({
+          url: settings.thumbnail.path,
+          method: 'GET',
+          responseType: 'stream',
+        })
+      ).data;
+      const warning = await handleYoutubeThumbnail(
         () =>
           this.runInConcurrent(async () =>
             youtubeClient.thumbnails.set({
               videoId,
               media: {
-                body: (
-                  await axios({
-                    url: settings?.thumbnail?.path,
-                    method: 'GET',
-                    responseType: 'stream',
-                  })
-                ).data,
+                body: thumbnail,
               },
             })
           ),
         firstPost.id,
         videoId
       );
+      return [
+        {
+          id: firstPost.id,
+          releaseURL: `https://www.youtube.com/watch?v=${videoId}`,
+          postId: videoId,
+          status: 'success',
+          ...(warning ? { warning } : {}),
+        },
+      ];
     }
 
     return [
diff --git a/libraries/react-shared-libraries/src/translation/locales/en/translation.json b/libraries/react-shared-libraries/src/translation/locales/en/translation.json
index ad1642a..492546a 100644
--- a/libraries/react-shared-libraries/src/translation/locales/en/translation.json
+++ b/libraries/react-shared-libraries/src/translation/locales/en/translation.json
@@ -1,5 +1,22 @@
 {
   "calendar": "Calendar",
+  "calendar_channel_filter": "Channels",
+  "calendar_channels_selected": "{{count}} selected",
+  "calendar_show_all_channels": "Show all channels",
+  "calendar_clear_selection": "Clear selection",
+  "calendar_select_all": "Select all",
+  "calendar_search_channels": "Search channels",
+  "calendar_no_customer": "No customer",
+  "calendar_all_customers": "All customers",
+  "calendar_channel_refresh_needed": "Refresh needed",
+  "calendar_channel_disabled": "Disabled",
+  "calendar_channel_selection_limit": "You can select up to {{count}} channels.",
+  "calendar_compact_representation": "Compact calendar view",
+  "calendar_no_posts_for_channels": "No posts match the selected channels",
+  "calendar_request_error": "Unable to load the calendar.",
+  "calendar_retry": "Retry",
+  "integration_list_request_error": "Unable to load channels.",
+  "integration_list_retry": "Retry",
   "webhooks": "Webhooks",
   "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhooks are a way to get notified when something happens in Postiz via\n an HTTP request.",
   "name": "Name",
@@ -335,6 +352,14 @@
   "preview_post": "Preview Post",
   "post_statistics": "Post Statistics",
   "draft": "Draft",
+  "calendar_unknown_channel": "Unknown channel",
+  "calendar_status_draft": "Draft",
+  "calendar_status_scheduled": "Scheduled",
+  "calendar_status_published": "Published",
+  "calendar_status_error": "Error",
+  "calendar_post_actions": "Actions for {{channel}}",
+  "calendar_error_generic": "An error occurred while publishing this post",
+  "calendar_error_details": "Publishing error details",
   "week_number": "Week {{number}}",
   "top_title_edit_webhook": "Edit webhook",
   "top_title_add_webhook": "Add webhook",
@@ -696,4 +721,4 @@
   "back": "Back",
   "get_started": "Get Started",
   "kick_select_channel": "Select Channel"
-}
\ No newline at end of file
+}
diff --git a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json
index 2d6d355..fbad9f0 100644
--- a/libraries/react-shared-libraries/src/translation/locales/vi/translation.json
+++ b/libraries/react-shared-libraries/src/translation/locales/vi/translation.json
@@ -1,5 +1,22 @@
 {
   "calendar": "Lịch",
+  "calendar_channel_filter": "Kênh",
+  "calendar_channels_selected": "{{count}} đã chọn",
+  "calendar_show_all_channels": "Hiện tất cả kênh",
+  "calendar_clear_selection": "Xóa chọn",
+  "calendar_select_all": "Chọn tất cả",
+  "calendar_search_channels": "Tìm kiếm kênh",
+  "calendar_no_customer": "Không có khách hàng",
+  "calendar_all_customers": "Tất cả khách hàng",
+  "calendar_channel_refresh_needed": "Cần kết nối lại",
+  "calendar_channel_disabled": "Đã tắt",
+  "calendar_channel_selection_limit": "Bạn có thể chọn tối đa {{count}} kênh.",
+  "calendar_compact_representation": "Chế độ danh sách thu gọn",
+  "calendar_no_posts_for_channels": "Không có bài viết nào phù hợp với các kênh đã chọn",
+  "calendar_request_error": "Không thể tải lịch.",
+  "calendar_retry": "Thử lại",
+  "integration_list_request_error": "Không thể tải các kênh.",
+  "integration_list_retry": "Thử lại",
   "webhooks": "Webhook",
   "webhooks_are_a_way_to_get_notified_when_something_happens_in_postiz_via_an_http_request": "Webhook là một cách để nhận thông báo khi có sự kiện xảy ra trong Postiz thông qua một yêu cầu HTTP.",
   "name": "Tên",
@@ -335,6 +352,14 @@
   "preview_post": "Xem trước bài viết",
   "post_statistics": "Thống kê bài viết",
   "draft": "Bản nháp",
+  "calendar_unknown_channel": "Kênh không xác định",
+  "calendar_status_draft": "Nháp",
+  "calendar_status_scheduled": "Đã lên lịch",
+  "calendar_status_published": "Đã đăng",
+  "calendar_status_error": "Lỗi",
+  "calendar_post_actions": "Hành động cho {{channel}}",
+  "calendar_error_generic": "Đã xảy ra lỗi khi đăng bài viết này",
+  "calendar_error_details": "Chi tiết lỗi đăng bài",
   "week_number": "Tuần {{number}}",
   "top_title_edit_webhook": "Chỉnh sửa webhook",
   "top_title_add_webhook": "Thêm webhook",
