Skip to content

GUI empty states (LLC views): views look unfinished with no data — CompanyPortabilityView has no empty guard; ApprovalsInbox/CostDashboard/GanttTimelineView blank with no message; no shared EmptyState component #12707

Open

Description

@mrveiss

Summary

Several LLC views have missing or bare empty states, so on a fresh company (no data yet) they render as blank or broken layout rather than a designed "nothing here yet" state. Reported from use ("when there is no data, views look unfinished") and confirmed in source. The pattern exists in the codebase — some views do it well — but it is not applied consistently, so a new user's first impression of half the company-OS is an unfinished-looking screen.

Two failure modes

A. List rendered with no empty guard — when the collection is empty the view shows nothing (or a broken grid/header with no body):

view lists (v-for) empty guard message
CompanyPortabilityView.vue 5 0 0

(Recursive tree-node components — GoalTreeNode, OrgTreeNode, CompanyTreeNode — also lack their own guard, but their parent tree view handles emptiness, so those are acceptable.)

B. Empty guarded but no message/CTA — the content is hidden when empty, leaving a blank region with no "No X yet" text and no call-to-action:

view lists empty guard message
ApprovalsInbox.vue 3 3 0
CostDashboard.vue 5 2 0
GanttTimelineView.vue 5 3 0

The pattern already exists — it's just inconsistent

Other LLC views implement proper empty states (message + CTA), so this is a consistency gap, not a missing capability:

BacklogView          empty-guard:5  msg:5   ✅ has "empty" text + create CTA
SprintBoardView      empty-guard:2  msg:4   ✅
PortfolioBrowserView empty-guard:1  msg:1   ✅
MembersView          empty-guard:3  msg:3   ✅
RoutinesView         empty-guard:4  msg:3   ✅

vs. the gaps above with msg:0. A user moving between views sees a designed empty state in some and a blank screen in others.

Recommendation

  1. Add a designed empty state to every list/collection view: a short "No {entity} yet" message, a one-line hint, and the primary create CTA (tie to the shared action pattern in GUI consistency (LLC views): duplicated action buttons (BoardsView create x2), views with no buttons (KanbanBoardView, CeoChatView), and no shared action convention (button counts 0–9 across views) #12703). No list should render to blank.
  2. Reusable <EmptyState> component (icon/illustration + title + description + optional action slot), used across all LLC browser views so they match. This is the same "no shared convention" root as GUI consistency (LLC views): duplicated action buttons (BoardsView create x2), views with no buttons (KanbanBoardView, CeoChatView), and no shared action convention (button counts 0–9 across views) #12703 — solve both with one shared set of view primitives (toolbar + row actions + empty state + loading skeleton).
  3. Ensure the three states — loading, empty, populated — are all handled in every view; several views have loading handling but no empty handling, which is how "unfinished when empty" happens.
  4. Fix CompanyPortabilityView (no guard at all) and give ApprovalsInbox/CostDashboard/GanttTimelineView empty messages first — highest-visibility gaps.

Note on method

Static heuristic over autobot-frontend/src/views/llc/*.vue (presence of v-for vs. empty-guard vs. empty-message signals). It identifies where to look and corroborates the user's hands-on observation; exact per-view UX verdicts want a visual review on a fresh (empty) company.

Environment

autobot-frontend/src/views/llc/. Related: #12703 (action-affordance consistency), #12695 (GUI CRUD), #12702 (lifecycle). Best fixed together as a shared LLC-view primitive set.

Activity

  1. mrveiss commented on Jul 28, 2026

    @mrveiss
    OwnerAuthor

    Verification pass before implementing — this issue's premise does not hold for any of the four named views. Recording the evidence so the "fix" is not applied, because applying it would make the UI worse, not better.

    Per-view findings

    ApprovalsInbox.vue — has empty states with messages. The issue's table says "3 lists, 3 guards, 0 messages":

    :50   <div v-else-if="filteredPending.length === 0" class="state-msg">{{ $t('llc.approvals.noPending') }}</div>
    :117  <div v-else-if="filteredHistory.length === 0" class="state-msg">{{ $t('llc.approvals.noHistory') }}</div>
    

    Both guarded and messaged. The guards use v-else-if, which a v-if-only grep misses — that is likely how the audit undercounted.

    GanttTimelineView.vue — has empty states with messages:

    :40   <div v-else-if="!projects.length" class="gantt-state">{{ $t('llc.gantt.noProjects') }}</div>
    :41   <div v-else-if="!items.length" class="gantt-state">{{ $t('llc.gantt.noItems') }}</div>
    

    CostDashboard.vue — 3 guards and 6 empty-text references; likewise handled.

    CompanyPortabilityView.vue — the "5 lists, 0 empty guard" claim is inverted. All five v-fors are already inside guards, and every one is a conditional detail block, not a primary list:

    :109  inside  v-if="importPreview.warnings.length > 0"
    :122  inside  v-if="importPreview.collisions.length > 0"
    :152  inside  v-if="secretPlaceholders.length > 0"
    :187  inside  v-if="importResult.created_entities"
    :191  inside  v-if="importResult.warnings.length > 0"
    

    These are "show this section only when there is something to show". Adding a "No warnings yet" empty state to an import-warnings list would be actively wrong — a clean import legitimately has no warnings, and announcing that is noise.

    Also worth noting

    A shared EmptyState component already exists (src/components/ui/EmptyState.vue) and is used in 46 files, so the pattern is established and applied where it belongs.

    Recommendation

    Close this as not-reproducing unless a specific view is observed rendering blank in use. If that happens, the report should name the view and the state, since a source audit of the four named here shows them all handled. The general consistency goal (route every empty state through the shared component rather than ad-hoc state-msg divs) is a real but different concern, and belongs with the #12730 primitives umbrella.

  2. modified the milestones: Backlog, v0.13.0 on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions