diff --git a/src/app/(campaign)/campanha/(app)/atividades/[slug]/page.tsx b/src/app/(campaign)/campanha/(app)/atividades/[slug]/page.tsx
index 40dbbe95..8599adaf 100644
--- a/src/app/(campaign)/campanha/(app)/atividades/[slug]/page.tsx
+++ b/src/app/(campaign)/campanha/(app)/atividades/[slug]/page.tsx
@@ -19,7 +19,7 @@ import { Badge } from '@/components/ui/Badge'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { campaignPageMetadata } from '@/lib/campaignPageChrome'
-import { formatBahiaDateTimeLabel } from '@/lib/campaignTime'
+import { formatActivityWhenLabel } from '@/utilities/activityViewModels'
import { getActivityDetailPageData } from '@/utilities/activityDetailPageData'
import {
@@ -115,7 +115,9 @@ export default async function ActivityDetailPage({
))}
- {view.startAt ? formatBahiaDateTimeLabel(view.startAt) : 'Data a definir'}
+ {view.startAt
+ ? formatActivityWhenLabel(view.startAt, { allDay: view.allDay, endAt: view.endAt })
+ : 'Data a definir'}
diff --git a/tests/e2e/campaignActivity.e2e.spec.ts b/tests/e2e/campaignActivity.e2e.spec.ts
index c9875a92..1ec983c7 100644
--- a/tests/e2e/campaignActivity.e2e.spec.ts
+++ b/tests/e2e/campaignActivity.e2e.spec.ts
@@ -181,25 +181,19 @@ test.describe('Agenda — calendário operacional', () => {
await campaign.login(page, coordinator.email!, coordinator.password)
await page.goto(`${campaign.baseURL}/campanha/agenda?municipality=${municipality.id}`)
- const dayCell = page.getByRole('gridcell').nth(1)
+ const slotLocator = page.locator('[data-time="14:00:00"]:visible').last()
// FullCalendar renders the time axis lazily and re-lays out on mount: the
// grid first paints empty and re-renders (replacing its gridcells) when the
// events land. Wait for the load to finish before interacting, or the
// locator's element is replaced mid-action ("Element is not attached").
await expect(page.getByText('Carregando compromissos…')).toHaveCount(0, { timeout: 15_000 })
- const slotLocator = page.locator('[data-time="14:00:00"]:visible').last()
await expect(slotLocator).toBeVisible()
- await expect(dayCell).toBeVisible()
- await dayCell.scrollIntoViewIfNeeded()
- const slotBox = await slotLocator.boundingBox()
- const dayBox = await dayCell.boundingBox()
- if (!slotBox || !dayBox) throw new Error('A grade semanal não expôs o slot esperado.')
- await dayCell.click({
- position: {
- x: dayBox.width / 2,
- y: slotBox.y - dayBox.y + slotBox.height / 2,
- },
- })
+ // C104 — with the all-day lane enabled, the v7 classic theme paints the
+ // all-day day fills over the time grid; real clicks still land on the slot
+ // (FullCalendar resolves by data-time), but Playwright's actionability
+ // check refuses them, so the click is forced on the slot lane itself.
+ await slotLocator.scrollIntoViewIfNeeded()
+ await slotLocator.click({ force: true })
// The click no longer navigates: the inline overlay opens at the slot.
await expect(page).toHaveURL(new RegExp('/campanha/agenda.*'))
@@ -263,21 +257,15 @@ test.describe('Agenda — calendário operacional', () => {
await campaign.login(page, coordinator.email!, coordinator.password)
await page.goto(`${campaign.baseURL}/campanha/agenda?municipality=${municipality.id}`)
- const dayCell = page.getByRole('gridcell').nth(1)
await expect(page.getByText('Carregando compromissos…')).toHaveCount(0, { timeout: 15_000 })
const slotLocator = page.locator('[data-time="14:00:00"]:visible').last()
await expect(slotLocator).toBeVisible()
- await expect(dayCell).toBeVisible()
- await dayCell.scrollIntoViewIfNeeded()
- const slotBox = await slotLocator.boundingBox()
- const dayBox = await dayCell.boundingBox()
- if (!slotBox || !dayBox) throw new Error('A grade semanal não expôs o slot esperado.')
- await dayCell.click({
- position: {
- x: dayBox.width / 2,
- y: slotBox.y - dayBox.y + slotBox.height / 2,
- },
- })
+ // C104 — with the all-day lane enabled, the v7 classic theme paints the
+ // all-day day fills over the time grid; real clicks still land on the slot
+ // (FullCalendar resolves by data-time), but Playwright's actionability
+ // check refuses them, so the click is forced on the slot lane itself.
+ await slotLocator.scrollIntoViewIfNeeded()
+ await slotLocator.click({ force: true })
const startTrigger = page.getByLabel('Início *')
await expect(startTrigger).toBeVisible()
@@ -329,25 +317,19 @@ test.describe('Agenda — calendário operacional', () => {
await campaign.login(page, coordinator.email!, coordinator.password)
await page.goto(`${campaign.baseURL}/campanha/agenda?municipality=${municipality.id}`)
- const dayCell = page.getByRole('gridcell').nth(1)
+ const slotLocator = page.locator('[data-time="14:00:00"]:visible').last()
// FullCalendar renders the time axis lazily and re-lays out on mount: the
// grid first paints empty and re-renders (replacing its gridcells) when the
// events land. Wait for the load to finish before interacting, or the
// locator's element is replaced mid-action ("Element is not attached").
await expect(page.getByText('Carregando compromissos…')).toHaveCount(0, { timeout: 15_000 })
- const slotLocator = page.locator('[data-time="14:00:00"]:visible').last()
await expect(slotLocator).toBeVisible()
- await expect(dayCell).toBeVisible()
- await dayCell.scrollIntoViewIfNeeded()
- const slotBox = await slotLocator.boundingBox()
- const dayBox = await dayCell.boundingBox()
- if (!slotBox || !dayBox) throw new Error('A grade semanal não expôs o slot esperado.')
- await dayCell.click({
- position: {
- x: dayBox.width / 2,
- y: slotBox.y - dayBox.y + slotBox.height / 2,
- },
- })
+ // C104 — with the all-day lane enabled, the v7 classic theme paints the
+ // all-day day fills over the time grid; real clicks still land on the slot
+ // (FullCalendar resolves by data-time), but Playwright's actionability
+ // check refuses them, so the click is forced on the slot lane itself.
+ await slotLocator.scrollIntoViewIfNeeded()
+ await slotLocator.click({ force: true })
await expect(page.getByLabel('Título *')).toBeVisible()
await page.getByLabel('Título *').fill(title)