feat(web): LiveView frontend (design system + Maraca/Ybira/Jaci screens) - #159
Conversation
Server-rendered LiveView UI over the existing contexts, dark Tupi-Guarani
vault aesthetic from Penpot ("Cofre da Comunidade"), mobile-first.
- Design system: tokens.css (Penpot 1:1), core_components, icons, layouts
(app shell + bottom-nav + auth), esbuild pipeline (no Tailwind), fonts.
- Maraca: setup wizard, login, invite create/accept (controllers + LiveViews).
- Ybira: file browser (list/grid, sort, rename/move/trash, DnD), upload,
trash, storage usage + quota.
- Jaci: photo gallery (grid + timeline + viewer).
- Home, members, account screens. pt-BR via gettext.
- Functional Core / Imperative Shell on LiveViews and JS hooks; tests across
controllers and LiveViews.
- Build output priv/static/assets now gitignored (regenerated by assets.build).
WalkthroughO PR cria a base do frontend com tokens, CSS modular, JS do LiveView, componentes compartilhados e layout raiz. Também reestrutura identidade e acesso com login, setup, convite, sessão e membros via zelador(a), amplia Jaci/Ybira com vídeo, renomeação, paginação por offset e métricas, e entrega LiveViews autenticadas para home, arquivos, upload, galeria, conta, armazenamento, lixeira e previews, com testes cobrindo esses fluxos. Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related issues
Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 48
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@assets/css/base.css`:
- Around line 92-102: The global CSS rules using the universal selector (*) that
apply scrollbar-width: none and *::-webkit-scrollbar styling hide scrollbars
across the entire interface, creating an accessibility barrier for users who
depend on visual scrollbar indicators to navigate content with overflow. Remove
these global scrollbar-hiding rules (the scrollbar-width property and the
*::-webkit-scrollbar pseudo-element block) to restore visible scrollbars by
default, ensuring all users can clearly identify when content is scrollable.
In `@assets/css/components.css`:
- Around line 158-159: The CSS is using raw style values instead of semantic
design tokens, causing visual inconsistency across the system. In
assets/css/components.css lines 158-159, replace the hardcoded rgb() color value
for the background with the appropriate semantic color token from your design
system tokens, while ensuring all style properties use token variables
consistently. In assets/css/utilities.css lines 239-246, replace all hardcoded
pixel-based breakpoint values with the semantic breakpoint tokens defined in
your design system rather than raw px literals. Both locations should
exclusively use var(--token-name) references to maintain consistency with your
semantic token contract.
- Around line 24-35: Interactive controls in the file are missing dedicated
`:focus-visible` states for keyboard navigation accessibility. Add
`:focus-visible` pseudo-class styling to all button and control selectors to
provide a clear, visible focus indicator independent of browser defaults. Apply
this pattern to the `.btn--primary` class (lines 24-35) and to all other
interactive control selectors mentioned in the comment at lines 109-122,
171-176, 638-654, and 1017-1030, ensuring each gets an appropriate focus style
(such as outline or box-shadow) that meets visibility requirements.
- Line 819: The `word-break: break-word;` property in the CSS components file is
using a deprecated CSS specification. Replace this property with `overflow-wrap:
anywhere;` which provides equivalent, modern behavior that is well-supported
across current browsers. Optionally, you may also include `word-break: normal;`
alongside it for explicit clarity, since `break-word` is now considered
outdated.
In `@assets/css/layouts.css`:
- Around line 21-31: Replace all raw CSS values in the layouts.css file with
semantic tokens from tokens.css. In the page-in keyframes animation (lines
21-31) and other affected locations (105-106, 196-196, 214-217, 233-234,
243-244, 276-276), replace hardcoded values such as pixel measurements (like the
8px in translateY), millisecond durations (like 220ms), opacity numbers, and
other magic numbers with corresponding semantic token references using the
var(...) syntax. For example, replace hardcoded animation durations, spacing
values, and easing functions with their semantic token equivalents from the
design system tokens.css file to maintain consistency across the codebase.
- Line 220: The `background-attachment: fixed` property at line 220 causes
performance degradation on mobile and low-end hardware due to heavy repaint
operations and janky scrolling behavior. Either remove this property entirely or
wrap it in a media query to apply it only on larger desktop screens, ensuring
the project maintains fluid performance across the target hardware spectrum
including mobile and resource-constrained devices.
In `@assets/js/app.js`:
- Around line 7-9: The csrfToken initialization does not guard against the case
where the meta tag with name='csrf-token' is absent from the DOM. When
querySelector returns null for a missing element and getAttribute is called on
it, the application initialization will crash. Add a null check after the
querySelector call to verify the meta element exists before calling getAttribute
on it, and handle the failure case appropriately (either by throwing a
descriptive error or setting a fallback value).
In `@assets/js/hooks.js`:
- Around line 89-123: The DnD mounted method only clears the `dragged` state
within the drop event listener, which means if a drag operation is cancelled or
ends outside a valid drop target, the stale state persists and can cause
incorrect move-item calls on the next drop. Add a dragend event listener to the
this.el element that unconditionally resets `dragged` to null, ensuring clean
state for subsequent drag operations regardless of how the current drag
terminates.
In `@CLAUDE.md`:
- Around line 23-24: Update the Elixir version declaration in CLAUDE.md line 23
to match the actual project requirement specified in mix.exs. Change "Elixir
1.20 / OTP 28" to "Elixir 1.16 / OTP 26" so that the documentation accurately
reflects the real target version constraint of ~> 1.16 and prevents contributors
from using unavailable APIs.
In `@lib/taina_web/components/core_components.ex`:
- Line 213: The variant `admin` in the badge component's attribute definition
conflicts with the project's governance naming conventions. Replace `admin` with
`zelador` in the values list of the attr definition for the variant attribute in
the badge component. Additionally, locate and update any CSS classes or styling
rules that specifically target the admin variant (such as class names like
badge-admin or similar patterns) to use zelador instead. Finally, search all
callsites throughout the codebase that pass `variant: admin` or `variant:
:admin` to badge components and update them to use `variant: zelador` to
maintain consistent domain language alignment with the social roles nomenclature
guidelines.
- Around line 375-386: The menu list container in the core_components menu has a
hardcoded `hidden` attribute that conflicts with the JavaScript toggle behavior.
Remove the `hidden` attribute from the div element with id `#{`@id`}-list` and
role="menu" to allow the JS.toggle function to properly manage visibility
through the display property without accessibility inconsistencies. The toggle
should handle all show/hide state management without a static hidden attribute
interfering.
In `@lib/taina_web/components/layouts.ex`:
- Around line 57-65: The storage card display in layouts.ex can show negative
free space when used_bytes exceeds quota_bytes, and the percentage calculation
can exceed 100. To fix this, wrap the free space calculation (quota_bytes minus
used_bytes) with max(0, ...) to prevent negative values on the line displaying
free storage. Additionally, ensure the storage_percent function and any similar
percentage calculations throughout the component are capped at 100 by using
max(0, min(100, ...)) pattern, following the same approach already used in
lib/taina_web/live/files_live.ex for handling the "livres" (free) value.
- Around line 142-144: The footer text in the auth-layout component does not
explicitly frame casa's privacy commitment as a software and trust promise. The
current gettext message should be updated to clearly state that it is a
"promessa de software" (software promise) rather than implying a cryptographic
guarantee. Modify the text content within the gettext call on line 143 to
honestly frame the privacy commitment as a trust-based software promise, using
language like "promessa de software, não cadeado matemático" to make explicit
that this is a promise of good software practice and trust, not a mathematical
or cryptographic guarantee.
In `@lib/taina_web/components/layouts/root.html.heex`:
- Around line 8-10: The default title and suffix attributes in the live_title
component are hardcoded string literals instead of being localized through
gettext. Update the live_title component to wrap both the default="Tainá" and
suffix=" - Tainá" attributes with gettext/1 function calls to ensure these
user-facing strings are properly internationalized and maintain consistency with
the app's i18n guidelines.
In `@lib/taina_web/hooks.ex`:
- Around line 12-25: The on_mount function has excessive nesting depth that's
causing a Credo lint failure. Extract the storage_stats resolution logic into a
private helper function that takes the current_scope as a parameter and returns
the stats or nil. Replace the nested case statements and Ybira.storage_stats
call in the assign_new anonymous function with a call to this new private helper
function. This reduces the nesting depth while maintaining the same behavior and
logic for handling the scope validation and stats retrieval.
In `@lib/taina_web/live/account_live.ex`:
- Around line 36-37: The permission gate and label in the account_live.ex file
need to be updated to align with the zelador/morador role model. At lines 36-37,
replace the `Maraca.admin?/1` predicate call with `Maraca.zelador?/1` and change
the gettext("Admin") label to the appropriate zelador-related copy
(gettext("Zelador") or similar). Apply the same change at lines 45-47 where the
same pattern appears, ensuring both locations consistently use the zelador
predicate and corresponding label text instead of the admin-based terminology
throughout the file.
In `@lib/taina_web/live/file_preview_live.ex`:
- Around line 110-111: Replace all inline style attributes with hardcoded pixel
values and magic numbers in the file_preview_live.ex template with semantic
design token-based utility classes from the design system. Specifically, in the
div elements at lines 110-111 that contain inline styles like max-width: 720px,
min-height: 280px, overflow: hidden, and any similar inline styles in the
120-124 range, remove the style attributes and replace them with appropriate
semantic utility classes or design system tokens that represent these spacing,
sizing, and layout properties. This ensures consistency across the codebase and
eliminates raw px values and magic numbers from .heex templates as per the
coding guidelines.
- Around line 15-30: The mount/3 function's case statement only handles {:ok,
file} and {:error, :not_found} specifically, but Ybira.get_file/2 can return
other error reasons that will cause the LiveView to crash. Add a catch-all error
handler clause that matches {:error, _reason} (any reason) to handle unexpected
errors gracefully. This fallback clause should use Phoenix.LiveView.put_flash
and Phoenix.LiveView.redirect to display an error message and redirect to the
arquivos page, similar to how the :not_found case is handled, ensuring the
LiveView remains available even when unexpected errors occur.
In `@lib/taina_web/live/files_live.ex`:
- Around line 223-224: The sort_arrow function uses Unicode arrow characters (↑
and ↓) which violate the project's ASCII-only coding guideline for UI copy.
Replace the Unicode arrows in both sort_arrow clauses with ASCII alternatives
such as "asc" and "desc" (or "->" if preferred) to maintain consistency with the
repository's standards for plain ASCII text.
- Around line 116-123: The event handlers in the FilesLive module lack defensive
handling for invalid payloads and unexpected state values. The handle_event
functions for "rename-item", and the other affected handlers at lines 133-140
and 162-194, use case statements that will crash with CaseClauseError if the
kind or confirm values are invalid or missing. Add a catch-all clause with _ ->
{:error, :invalid_action} pattern to each case statement in
handle_event("rename-item", ...), the move handler, and the delete confirmation
handler to gracefully reject unexpected inputs. Additionally, ensure that any
error cases properly respond with flash messages to the user instead of allowing
crashes to propagate.
- Around line 27-28: The code uses strict pattern matching with `{:ok, ...} =`
for the `Ybira.list_folder_contents` function calls, which causes the LiveView
to crash with a MatchError if the function returns an error tuple instead of
success. Replace the strict pattern match with a case statement that handles
both success and error cases. In the error branch, use `put_flash` to display an
error message to the user and maintain a safe state by assigning default or
empty values to the socket assigns. This fix needs to be applied at three
locations in the file: at lines 27-28 (the initial call to
list_folder_contents), at lines 63-64 (another call to list_folder_contents),
and at line 85 (another instance of the same pattern).
In `@lib/taina_web/live/gallery_live.ex`:
- Around line 230-238: Add accessible labels to icon-only controls to comply
with accessibility guidelines. In the photo gallery links (lines 230-238 and
270-273) where the play icon is displayed for videos, add an `aria-label`
attribute to the `.link` component using `gettext` to provide a screen
reader-friendly name. Similarly, in the viewer navigation buttons (lines
321-326), add `aria-label` attributes with appropriate translated text using
`gettext`. This ensures that keyboard and screen reader users can understand the
purpose of each icon-only button or link.
- Around line 58-65: The timeline viewer navigation breaks because photo_ids is
not synchronized with the timeline groups across multiple locations. In the
ensure_timeline function, extract and assign all photo_ids from the groups
returned by Jaci.timeline so they stay in sync. At the fallback location around
lines 82-83, ensure that the fallback assigns all available photo_ids rather
than creating a single-item list, which currently breaks prev/next and swipe
navigation. Apply the same synchronization pattern at lines 101-108 to ensure
photo_ids always reflects the current timeline groups being displayed.
- Around line 138-139: The handle_event function for "confirm-delete" in
GalleryLive does not validate that socket.assigns.current exists before
accessing socket.assigns.current.public_id, which can cause an exception and
crash the LiveView process if the event fires in a stale state or out of
expected order. Add a guard clause, pattern matching, or conditional check to
ensure that socket.assigns.current is present and has the public_id value before
attempting to call Ybira.delete_file. If the assigns are missing, return an
appropriate error response or noop rather than allowing the crash.
- Line 205: The function call to `effective_datetime()` uses the full module
path `Taina.Jaci.Timeline` instead of the alias `Jaci.Timeline` that is already
established at the top of the file. To align with the consistent pattern used
elsewhere in the file and comply with credo strict rules, replace the
`Taina.Jaci.Timeline.effective_datetime()` call with
`Jaci.Timeline.effective_datetime()` to use the alias as intended.
In `@lib/taina_web/live/home_live.ex`:
- Around line 17-25: The mount/3 function in home_live.ex uses strict pattern
matching on lines 17-18 that crashes the LiveView if Ybira.list_recent or
Ybira.storage_stats_by_kind return error tuples. Replace the direct pattern
matches with case statements that handle both {:ok, value} and {:error, _} cases
for each call. When either call returns an error, assign an error state to the
socket instead of crashing, and render an appropriate error/permission view
using the <.empty_state> component as specified in the coding guidelines, rather
than the happy path with recent and by_kind data.
In `@lib/taina_web/live/invite_accept_live.ex`:
- Line 25: Remove the inline style attribute containing align-items: center from
the div element in invite_accept_live.ex and replace it with an appropriate
utility class from the design system (such as a center or items-center utility
class). Add this utility class to the existing class attribute alongside col,
gap-3, text-center, mt-10, and mb-8 to maintain consistency with the design
system tokens and comply with the coding guidelines that require using utility
classes instead of inline styles in .heex files.
In `@lib/taina_web/live/invite_live.ex`:
- Around line 37-39: The handle_event function for "set-role" in the
invite_live.ex file only has a guard clause that accepts "member" or "admin"
roles. If any other role value is passed, it raises a FunctionClauseError and
crashes the LiveView process. Add a fallback clause for handle_event with the
same "set-role" event name but without the guard condition, so it matches any
invalid role value and returns {:noreply, socket} (with an optional flash error
message), preventing the crash while gracefully handling invalid inputs.
In `@lib/taina_web/live/login_live.ex`:
- Around line 49-50: Remove inline style attributes from the template and
replace them with utility classes from the design system. In
lib/taina_web/live/login_live.ex at lines 49-50, the div element contains a raw
inline style `style="align-items: center;"` that should be replaced with an
appropriate utility class (such as `center` or an equivalent flexbox utility
from your design system). Apply the same fix at lines 83-84 where this pattern
also occurs. Follow the coding guideline that .heex templates must use utility
classes for layout and spacing rather than raw inline style values.
In `@lib/taina_web/live/members_live.ex`:
- Around line 41-47: The first condition in the `member_meta/2` function
incorrectly labels the current user as "Administração, você" regardless of
whether they actually have admin role. Modify the first condition to only return
this label when the member is the current user (member.id == scope.ava.id) AND
the member has admin role (member.role == :admin), otherwise allow the logic to
continue to the subsequent conditions to determine the correct label based on
their actual role and status.
In `@lib/taina_web/live/setup_live.ex`:
- Around line 139-142: The Portuguese copy in the setup page uses
"administração" and language suggesting unilateral control ("controla tudo"),
which conflicts with the project's privacy and collective governance model.
Update both the heading at lines 139-142 and the related text at line 217 in
lib/taina_web/live/setup_live.ex to reframe the role as "zeladoria/zelador(a)"
(caretaker/steward) instead of admin, and remove language suggesting power or
control. Ensure the copy clarifies that the zelador has no special data access
rights or unilateral authority, emphasizing stewardship and community care
rather than administrative control.
- Around line 43-53: The `handle_event("next", ...)` function calls
`validate_step(socket.assigns.step, data)` without guarding against unexpected
step values. If the step is not 1 or 2, `validate_step/2` will raise a
FunctionClauseError and crash the LiveView session. Add a catch-all clause to
`validate_step/2` that returns a map of errors for any unexpected step value,
ensuring the function handles all possible step values gracefully rather than
crashing. This defensive fix should be applied to the `validate_step/2` function
definition to cover all call sites where it is used, including both
handle_event("next") at lines 43-53 and the other applicable location at lines
59-70.
In `@lib/taina_web/live/storage_live.ex`:
- Line 83: The div element in storage_live.ex at line 83 contains an inline
style attribute with a raw px value (max-width: 640px), which violates the
coding guideline requiring semantic tokens for styling in .heex templates.
Remove the inline style attribute from the div with classes "col gap-5 mx-auto
w-full" and replace the raw max-width value with an appropriate semantic
token-based utility class that maintains design system consistency without magic
numbers in the markup.
- Around line 26-34: The load_stats/1 function uses strict pattern matching on
{:ok, ...} which will crash the LiveView if Ybira.storage_stats or
Ybira.storage_stats_by_kind returns an error. Refactor load_stats/1 to return
{:ok, socket} | {:error, socket} by handling both success and error cases from
the Ybira function calls, assigning an error state to the socket when failures
occur. Update the caller to handle the error tuple and conditionally render the
<.empty_state> component when an error state is present, following the coding
guidelines that require explicit empty/error states instead of only the happy
path. Apply this same defensive error handling pattern to any other locations in
the file (around lines 83-97) where similar strict pattern matching on {:ok,
...} tuples occurs.
In `@lib/taina_web/live/trash_live.ex`:
- Line 94: Replace the inline style attribute `style="max-width: 640px;"` on the
div element with class "col gap-4 mx-auto w-full" with the appropriate semantic
token or design system class instead of using raw px values. Remove the
hardcoded `max-width: 640px;` style and apply a design system class or token
that represents the same max-width constraint, following the coding guideline
that semantic tokens must be used in .heex templates instead of raw px values or
magic numbers.
In `@lib/taina_web/live/upload_live.ex`:
- Around line 109-122: The live_file_input component in the dropzone has
conflicting accessibility attributes: both class="sr-only" (which hides visually
but keeps keyboard/screen reader access) and style="display: none;" (which
removes from DOM entirely). Remove the style="display: none;" attribute from the
live_file_input element to maintain keyboard accessibility and screen reader
support. Ensure the dropzone label itself has proper focus management and
visible focus states, with adequate touch target sizing (at least 44px per
guidelines).
In `@lib/taina_web/router.ex`:
- Around line 61-63: The pipe_through on line 62 references
`:require_authenticated` which doesn't exist as a defined pipeline and is
redundant since the on_mount hook already ensures authentication. Remove
`:require_authenticated` from the pipe_through array and keep only `:browser`,
as the modern Phoenix pattern delegates LiveView authentication to the on_mount
hook that is already present on line 65.
In `@lib/taina/jaci.ex`:
- Line 43: The filter logic in the jaci.ex module now includes video MIME types
(video/%) alongside image types, but the test suite lacks dedicated test cases
for this functionality. Add test cases to the module's test suite that validate
the inclusion of supported video MIME types and the exclusion of unsupported
MIME types to prevent silent regressions in the filter behavior for both image
and video content.
In `@lib/taina/jaci/behaviour.ex`:
- Around line 6-7: The documentation in the module header (lines 6-7) is
outdated and only mentions images (`mime_type image/*`), but the `Taina.Jaci`
implementation now supports both images and videos. Update the
contract/documentation text to accurately reflect that the module now handles
both image and video media types in the gallery, ensuring the public API
contract matches the actual implementation.
In `@lib/taina/ybira.ex`:
- Around line 369-380: The count_photos/1 function is incorrectly counting video
files in addition to image files. In the where clause of the Repo.aggregate
query, remove the condition that includes video mime types (the `or
like(f.mime_type, "video/%")` part), keeping only the image mime type check with
`like(f.mime_type, "image/%")`. This will ensure the function counts only photos
as intended by its name and contract.
- Around line 292-315: The list_folder_contents/3 function exceeds the maximum
nesting depth allowed by the linter. Extract the pagination logic that processes
the rows list and calculates the next_cursor into a new private helper function.
Move the block starting from the rows assignment through the next_cursor
calculation (including the if-do-else expression that checks if length(rows) >
limit) into a separate private function, then call this new function from
list_folder_contents/3 with the rows, limit, and offset parameters. This reduces
nesting depth without changing the function's behavior.
In `@lib/taina/ybira/behaviour.ex`:
- Around line 79-82: In the docstring comment (lines 79-82) that documents the
file renaming functionality, replace the term "admin" with "zelador" to align
with the domain-specific vocabulary established in your coding guidelines. This
ensures consistent use of `:zelador` and `:morador` atoms throughout the system
instead of the generic "admin" and "member" terminology, particularly important
since this module defines a public boundary for your contract.
In `@priv/repo/seeds.exs`:
- Around line 58-59: The temporary directory created at lines 58-59 is not
guaranteed to be cleaned up if an error occurs during the seed process, since
the cleanup with File.rm_rf!/1 only runs in the happy path. Wrap the entire seed
operation that uses the tmp variable (from its creation at lines 58-59 through
to the cleanup at line 128) in a try-after block to ensure File.rm_rf!(tmp) is
always executed in the after clause, regardless of whether the seed steps
succeed or fail.
- Around line 2-4: The seed file uses outdated role semantics with references to
admin and member patterns. Update the seed file to use the canonical governance
atoms :zelador and :morador instead of :admin and :member throughout the file.
This includes updating the seed data creation at the initial comment section
(lines 2-4) and all other locations where role assignments occur (lines 33-45),
ensuring both the documentation and the actual role atom assignments reflect the
new governance terminology consistently.
- Line 17: Remove all emoji and non-ASCII special characters from the IO.puts
messages in the seeds file to maintain consistency with the repository's plain
ASCII text standard. Replace the warning emoji (⚠️) and any other special
symbols with plain ASCII equivalents while preserving the Portuguese language
content and the intended warning/informational nature of the messages. This
applies to all IO.puts calls in the seeds file that currently contain emoji or
special characters.
In `@test/taina_web/controllers/session_controller_test.exs`:
- Around line 8-12: The global setup block always creates a tekoa fixture, which
prevents tests from covering the `:not_bootstrapped` error path in the create/2
action. Reorganize the tests by creating separate describe blocks with
context-specific setups: one describe block with the current setup that creates
tekoa for tests that require it, and a separate describe block without tekoa
creation to explicitly test the `:not_bootstrapped` error case and verify the
redirect to `/setup`. This ensures full coverage of the error contract while
maintaining test isolation.
In `@test/taina_web/live/gallery_live_test.exs`:
- Line 46: The assertion using the `or` operator on line 46 can pass even when
the HTML contains old state ("segunda.jpg"), masking navigation regressions.
Replace this assertion to be deterministic by first calling assert_patch/2 to
confirm the navigation occurred, then check that render(lv) contains only
"primeira.jpg" without using the `or` operator. This ensures the final state is
validated explicitly rather than allowing either condition to pass.
In `@test/taina_web/live/upload_live_test.exs`:
- Around line 49-63: The test currently validates that the UI displays an error
message when a file is rejected, but does not verify that the file was actually
blocked at the persistence level. Add an assertion after the HTML validation
using Ybira.list_files/1 (passing the ava identifier) to confirm that the
rejected file was not saved to the database. This ensures the rejection is
enforced at both the UI and domain levels, not just in the user-facing error
message.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f5fa9cb4-85e4-4118-8964-d8289ebd241a
⛔ Files ignored due to path filters (4)
mix.lockis excluded by!**/*.lockpriv/static/fonts/bricolage-grotesque-latin-wght.woff2is excluded by!**/*.woff2priv/static/fonts/jetbrains-mono-latin-wght.woff2is excluded by!**/*.woff2priv/static/fonts/schibsted-grotesk-latin-wght.woff2is excluded by!**/*.woff2
📒 Files selected for processing (82)
.gitignoreAGENTS.mdCLAUDE.mdassets/css/app.cssassets/css/base.cssassets/css/components.cssassets/css/layouts.cssassets/css/tokens.cssassets/css/utilities.cssassets/js/app.jsassets/js/hooks.jsassets/vendor/topbar.jsconfig/config.exsconfig/dev.exsconfig/prod.exsconfig/test.exslib/taina/jaci.exlib/taina/jaci/behaviour.exlib/taina/jaci/timeline.exlib/taina/maraca.exlib/taina/maraca/ava.exlib/taina/maraca/behaviour.exlib/taina/repo.exlib/taina/scope.exlib/taina/ybira.exlib/taina/ybira/behaviour.exlib/taina/ybira/file.exlib/taina/ybira/media.exlib/taina/ybira/mime_detector.exlib/taina/ybira/workers/purge_trash.exlib/taina/ybira/workers/rendition.exlib/taina_web.exlib/taina_web/auth.exlib/taina_web/components/core_components.exlib/taina_web/components/icons.exlib/taina_web/components/layouts.exlib/taina_web/components/layouts/root.html.heexlib/taina_web/controllers/file_controller.exlib/taina_web/controllers/invite_controller.exlib/taina_web/controllers/session_controller.exlib/taina_web/controllers/setup_controller.exlib/taina_web/gettext.exlib/taina_web/hooks.exlib/taina_web/live/account_live.exlib/taina_web/live/file_preview_live.exlib/taina_web/live/files_live.exlib/taina_web/live/gallery_live.exlib/taina_web/live/home_live.exlib/taina_web/live/invite_accept_live.exlib/taina_web/live/invite_live.exlib/taina_web/live/login_live.exlib/taina_web/live/members_live.exlib/taina_web/live/setup_live.exlib/taina_web/live/storage_live.exlib/taina_web/live/trash_live.exlib/taina_web/live/upload_live.exlib/taina_web/router.exmix.exspriv/gettext/pt_BR/LC_MESSAGES/default.popriv/gettext/pt_BR/LC_MESSAGES/errors.popriv/repo/seeds.exstest/support/conn_case.extest/support/fixtures.extest/taina/jaci_test.exstest/taina/maraca_members_test.exstest/taina/maraca_test.exstest/taina/rls_isolation_test.exstest/taina/ybira/rendition_test.exstest/taina/ybira_stats_test.exstest/taina/ybira_test.exstest/taina_web/controllers/session_controller_test.exstest/taina_web/controllers/setup_controller_test.exstest/taina_web/live/files_live_test.exstest/taina_web/live/gallery_live_test.exstest/taina_web/live/home_live_test.exstest/taina_web/live/invite_flow_test.exstest/taina_web/live/login_live_test.exstest/taina_web/live/members_live_test.exstest/taina_web/live/setup_live_test.exstest/taina_web/live/storage_live_test.exstest/taina_web/live/trash_live_test.exstest/taina_web/live/upload_live_test.exs
Act on the reviewable findings from the 48 CodeRabbit comments; skip the ones that conflict with the tekoa source of truth or were already fixed by the username/email-drop commit. CI green: - Flatten nested functions flagged by credo --strict (hooks on_mount, ybira list_folder_contents, upload handle_progress, maraca mint_reset_link); alias Taina.Jaci.Timeline in gallery. - Ignore the Gettext/Expo opaque-type dialyzer false positive. Crash-safety / error states: - Guard gallery confirm-delete against a stale @current. - Sync gallery photo_ids with the timeline so prev/next/swipe work. - file_preview mount handles a generic {:error, _}. - files_live rename/move/confirm-delete reject invalid payloads. - setup_live caps the step and tolerates an unknown step. - invite_live set-role gets a fallback clause. - Clamp storage card free bytes (>= 0) and percent (0..100). Naming / a11y / i18n / ASCII: - Rename badge variant admin -> zelador (attr + CSS + callsites); ybira behaviour docs admin -> zelador. - Replace sort arrows with chevron icons (ASCII rule). - aria-labels on gallery video items and viewer nav. - Drop the menu hidden attribute and the file-input display:none so keyboard/AT keep access; localize the root live_title. - background-attachment: scroll (Raspberry Pi feasibility). - CSRF meta guard in app.js; clear DnD drag state on dragend. - items-center utility instead of inline align-items. - Make the home avatar link to /conta. Domain / tests / docs: - count_photos counts image/* only, consistent with storage_stats_by_kind and its documented contract. - Jaci docs and tests cover video/*; add tmp_video_fixture. - upload test asserts rejected files are not persisted; gallery test asserts the final viewer state; session test covers the not-bootstrapped redirect. - Bump the elixir floor to ~> 1.20 to match CI and CLAUDE.md. Skipped (with reason): global scrollbar-hide and raw-value to token CSS migration (Penpot-sourced tokens, not piecemeal); focus-visible already global in base.css; breakpoint var() infeasible in media queries; privacy footer already honest; doc version downgrade contradicts the 1.20 target.
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (10)
test/taina_web/controllers/session_controller_test.exs (1)
2-2:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
async: trueaqui pode gerar flakiness com Tekoa única.Este módulo cria Tekoa no
setup; com a restriçãosingle_tekoa_enforcementdocumentada na suíte, rodar em paralelo com outros módulos que também criam Tekoa pode causar bloqueio/falha intermitente. Faz sentido tornar este móduloasync: false?Diff sugerido
-defmodule TainaWeb.SessionControllerTest do - use TainaWeb.ConnCase, async: true +defmodule TainaWeb.SessionControllerTest do + use TainaWeb.ConnCase, async: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/taina_web/controllers/session_controller_test.exs` at line 2, The session_controller_test.exs module uses `async: true` in its `use TainaWeb.ConnCase` statement, but the module creates Tekoa during setup. Since the test suite enforces a single_tekoa_enforcement constraint that prevents multiple Tekoa instances from running in parallel, running this test module concurrently with other test modules that also create Tekoa will cause intermittent failures. Change `async: true` to `async: false` in the use statement to ensure this module runs synchronously and avoids conflicts with other tests.lib/taina/maraca/access_request.ex (1)
43-46:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPadronizar a terminologia para
zelador/moradorem todo o módulo.Ainda ficaram referências a
admin/administradoresno moduledoc e exemplos. Podemos alinhar esses trechos com o modelo atual para evitar drift de contrato social/técnico?As per coding guidelines, usar
:zeladore:morador(nãoadmin/member) na nomenclatura de domínio.Also applies to: 72-73, 117-118, 141-142
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina/maraca/access_request.ex` around lines 43 - 46, Standardize the domain terminology throughout the access_request.ex module by replacing all references to admin/administrador with zelador and member with morador. This includes updating the iex example that shows admin soliciting access (lines 43-46 anchor location), and also update the related example sections at lines 72-73, 117-118, and 141-142 to consistently use the zelador and morador terminology instead of the admin/administrador nomenclature. Review the moduledoc and all code examples to ensure the domain language aligns with the current social/technical contract.Source: Coding guidelines
lib/taina_web/live/file_preview_live.ex (1)
14-36: 🛠️ Refactor suggestion | 🟠 MajorExtrair decisão de inicialização para helper puro, deixando o callback só com assign/redirect.
O
mount/3concentra toda a lógica de decisão (case/matchcom 3 branches) antes de fazerassigneput_flash. Conforme as guidelines, callbacks devem ficar finos—apenas operações de estado; todo branch/decisão deve viver em helpers puros.Poderíamos criar um helper que retorne
{:ok, state_map}ou{:error, error_type}, deixando omountapenas orquestrar osassign/put_flash/redirect. Ganha-se testabilidade e separação clara entre "qual é a decisão?" (helper) e "como aplicar?" (callback).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/live/file_preview_live.ex` around lines 14 - 36, Extract the case/match decision logic from the mount/3 callback into a pure helper function. The helper should accept the file lookup result and return either {:ok, state_map} or {:error, error_type} to represent the three branches (successful load, not_found error, and generic error). Then refactor mount/3 to call this helper and use its result only for orchestrating assign, put_flash, and redirect operations. This separates the decision-making logic (which belongs in the helper and is easily testable) from the state-mutation operations (which stay in the callback).Source: Coding guidelines
lib/taina/maraca/behaviour.ex (2)
78-109:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAlinhar
authenticate/3ao fluxo por username e ativação.O
SessionControllerchamaMaraca.authenticate(username, password, tekoa), mas este callback ainda documenta e tipa o fluxo antigo por e-mail confirmado (:email_not_confirmed). Podemos trocar o contrato parausername+activated_ate expor o erro novo esperado antes de estabilizar o behaviour?Diff sugerido
- Autentica usuário com email e senha. + Autentica pessoa com nome de usuário e senha. ... - - Email e senha devem estar corretos - - Email deve estar confirmado (confirmed_at não nulo) + - Nome de usuário e senha devem estar corretos + - Conta deve estar ativa (`activated_at` não nulo) ... - * `email` - Email do usuário + * `username` - Nome de usuário ... - * `{:error, :email_not_confirmed}` - Email ainda não confirmado + * `{:error, :not_activated}` - Convite ainda não aceito ... - `@callback` authenticate(String.t(), String.t(), Tekoa.t()) :: - {:ok, Ava.t()} | {:error, :invalid_credentials | :email_not_confirmed} + `@callback` authenticate(String.t(), String.t(), Tekoa.t()) :: + {:ok, Ava.t()} | {:error, :invalid_credentials | :not_activated}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina/maraca/behaviour.ex` around lines 78 - 109, The authenticate/3 callback in the behaviour module still documents and types the old email-based authentication flow with confirmed_at checks, but SessionController is calling it with username-based authentication that uses activated_at instead. Update the callback specification to reflect the actual username-based flow: change the first parameter documentation from email to username, update the business rules section to reference activated_at instead of confirmed_at, replace the :email_not_confirmed error atom with the appropriate activation error (such as :user_not_activated), and update the example calls to use username values instead of email addresses. This will align the contract with how SessionController actually invokes Maraca.authenticate.
159-168:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemover restos de
admindo contrato público.As docstrings misturam
zeladorcomadmin,admin_scopee “admins primeiro”, o que deixa ambíguo quem tem autoridade em Maracá. Podemos trocar esses trechos porzelador,zelador_scopee “zeladores primeiro”, além de atualizar “contas não confirmadas” para “convites pendentes”?As per coding guidelines, use
:zeladorinstead ofadminand keep the domain vocabulary consistent in Elixir code and docs.Also applies to: 220-238, 327-352, 520-555, 641-643
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina/maraca/behaviour.ex` around lines 159 - 168, Update the docstrings and comments in lib/taina/maraca/behaviour.ex to maintain consistent domain vocabulary by replacing all instances of admin-related terms with zelador-related terms. At lines 159-168 (anchor location), replace references to "admin" with "zelador", "admin_scope" with "zelador_scope", and "admins primeiro" with "zeladores primeiro"; additionally update any instances of "contas não confirmadas" to "convites pendentes". Apply the same terminology replacements at the sibling locations: lines 220-238, 327-352, 520-555, and 641-643 to ensure consistency throughout the public contract and documentation. This aligns the code with coding guidelines requiring `:zelador` usage instead of admin terminology.Source: Coding guidelines
lib/taina_web/controllers/setup_controller.ex (2)
46-52:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReservar espaço fora da cota inicial.
Usar todo
availcomostorage_quota_bytespermite que uploads ocupem também o espaço que banco, logs e sistema precisam. Em uma caixa comunitária modesta, isso pode virar pane por disco cheio; podemos aplicar uma reserva antes de salvar a cota?Diff sugerido
- {kbytes, ""} <- Integer.parse(avail) do - kbytes * 1024 + {kbytes, ""} <- Integer.parse(avail) do + free_bytes = kbytes * 1024 + reserve_bytes = min(10 * 1024 * 1024 * 1024, div(free_bytes, 10)) + + max(free_bytes - reserve_bytes, 1)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/controllers/setup_controller.ex` around lines 46 - 52, The current code in the setup_controller.ex file allocates the entire available disk space to storage_quota_bytes, which leaves no margin for database, logs, and system operations. In the pattern match where kbytes is successfully parsed and the function returns kbytes * 1024, apply a reserve buffer (a fixed percentage or absolute value) to deduct from the available space before calculating the storage quota, ensuring that a portion of disk space is preserved for system needs rather than being fully allocated to user uploads.
12-13:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winTrocar
adminporzeladorno comentário de domínio.O comentário ainda diz que “o admin reajusta”, mas este fluxo agora é de zelador. Podemos manter o vocabulário do cuidado também aqui?
As per coding guidelines, use
zelador, notadmin, in Elixir domain text.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/controllers/setup_controller.ex` around lines 12 - 13, Update the domain language in the comment at setup_controller.ex lines 12-13 where it currently refers to "admin reajusta depois" by replacing the word "admin" with "zelador" to align with the coding guidelines and the current domain terminology for administrative users in this system.Source: Coding guidelines
lib/taina_web/live/login_live.ex (1)
96-101:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUsar a linguagem de cuidado em vez de administração.
A modal diz “Quem administra a comunidade”, enquanto o restante do fluxo já usa “quem cuida”. Podemos manter a promessa social do produto trocando para “Quem cuida da comunidade te manda...” aqui também?
Diff sugerido
- "Quem administra a comunidade te manda um link (ou um QR code). É só abrir o link neste aparelho. A criação da conta acontece lá." + "Quem cuida da comunidade te manda um link (ou um QR code). É só abrir o link neste aparelho. A criação da conta acontece lá."As per coding guidelines, use the zelador/caretaker vocabulary instead of admin framing in user-facing domain text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/live/login_live.ex` around lines 96 - 101, The modal text in the invite-help modal uses "Quem administra a comunidade" which is inconsistent with the product's vocabulary guidelines that favor caretaker language. Replace "administra" with "cuida" in the gettext string within the modal with id "invite-help" so it reads "Quem cuida da comunidade te manda..." to maintain consistency with the rest of the user flow and align with the zelador/caretaker vocabulary standard.Source: Coding guidelines
lib/taina_web/live/gallery_live.ex (1)
49-60:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTratar erros de
Jacisem derrubar a galeria.Esses
{:ok, ...} = Jaci.*fazem a LiveView cair se o contexto retornar{:error, _}. Podemos envolver grade, linha do tempo e paginação em helpers que devolvam flash + estado vazio/seguro quando a leitura falhar?Also applies to: 97-109
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/live/gallery_live.ex` around lines 49 - 60, The pattern matching using {:ok, ...} = on the results from Jaci.list_photos and Jaci.timeline calls will crash the LiveView if these functions return an error tuple. Create helper functions that handle both success and error cases from Jaci function calls, returning either the expected data on success or safe default values (empty lists/cursors) along with a flash error message on failure. Replace the direct pattern matching calls in the load_photos function and the ensure_timeline function with calls to these new helpers that gracefully handle errors without crashing the gallery.lib/taina/ybira.ex (1)
292-296:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winPaginação por offset precisa sanitizar
:limite:offsetpara evitar cursor sem progresso.Topa normalizar esses parâmetros antes de paginar? Com
limit <= 0, onext_cursorpode repetir e prender o fluxo de "load more".Patch sugerido
def list_folder_contents(%Scope{} = scope, folder_public_id \\ nil, opts \\ []) do sort = normalize_sort(Keyword.get(opts, :sort)) - limit = Keyword.get(opts, :limit, `@default_limit`) - offset = Keyword.get(opts, :offset, 0) + limit = normalize_limit(Keyword.get(opts, :limit, `@default_limit`)) + offset = normalize_offset(Keyword.get(opts, :offset, 0)) @@ defp paginate_offset(query, limit, offset) do @@ if length(rows) > limit, do: {Enum.take(rows, limit), offset + limit}, else: {rows, nil} end + + defp normalize_limit(limit) when is_integer(limit) and limit > 0, do: limit + defp normalize_limit(_invalid), do: `@default_limit` + + defp normalize_offset(offset) when is_integer(offset) and offset >= 0, do: offset + defp normalize_offset(_invalid), do: 0Also applies to: 312-320
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina/ybira.ex` around lines 292 - 296, The `list_folder_contents` function extracts `:limit` and `:offset` from options without validating them, which can cause pagination issues when limit is zero or negative, leading to cursor progression problems. Normalize both the `limit` parameter to ensure it's a positive integer (using a minimum value if needed) and the `offset` parameter to ensure it's a non-negative integer before using them in pagination logic. Apply the same validation pattern to all other pagination methods in the codebase that extract these parameters from options.
♻️ Duplicate comments (2)
lib/taina_web/live/storage_live.ex (1)
26-34:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
load_stats/1ainda pode derrubar a LiveView em erro transitório.Nas Lines 28-29, o match estrito em
{:ok, ...}continua sem fallback. SeYbira.storage_stats/1ouYbira.storage_stats_by_kind/1retornar erro, a tela cai em vez de degradar com estado de erro.Patch sugerido
defp load_stats(socket) do scope = socket.assigns.current_scope - {:ok, stats} = Ybira.storage_stats(scope) - {:ok, by_kind} = Ybira.storage_stats_by_kind(scope) - - socket - |> assign(:stats, stats) - |> assign(:by_kind, by_kind) + with {:ok, stats} <- Ybira.storage_stats(scope), + {:ok, by_kind} <- Ybira.storage_stats_by_kind(scope) do + socket + |> assign(:stats, stats) + |> assign(:by_kind, by_kind) + |> assign(:stats_error, false) + else + _ -> + socket + |> assign(:stats, %{used_bytes: 0, quota_bytes: 0}) + |> assign(:by_kind, %{}) + |> assign(:stats_error, true) + end end🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/live/storage_live.ex` around lines 26 - 34, The load_stats/1 function uses strict pattern matching with {:ok, ...} on the return values of Ybira.storage_stats(scope) and Ybira.storage_stats_by_kind(scope), which will cause the LiveView to crash if either function returns an error. Replace the strict pattern matches with conditional logic (such as case statements or with patterns) that handles both success and error cases. When an error occurs, assign appropriate default or error state values to the socket instead of allowing the match to fail, ensuring the LiveView degrades gracefully rather than crashing.lib/taina_web/router.ex (1)
64-64:⚠️ Potential issue | 🔴 Critical | ⚡ Quick win
pipe_throughaponta para pipeline não definido.Na Line 64,
:require_authenticatednão existe como pipeline neste router. Isso quebra o encadeamento esperado de rotas autenticadas.Patch sugerido
- pipe_through [:browser, :require_authenticated] + pipe_through :browser🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/taina_web/router.ex` at line 64, The pipe_through directive at line 64 references a pipeline called `:require_authenticated` that is not defined in the router. Either define this pipeline in the router scope to handle authentication requirements for the routes that follow it, or replace `:require_authenticated` with an existing pipeline name that is already defined in this router file. Ensure the pipeline name matches an actual pipeline definition to properly chain the authenticated routes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/taina_web/controllers/session_controller.ex`:
- Line 12: The session_controller.ex and setup_controller.ex currently use
strict pattern matching in their create functions that causes
FunctionClauseError (500 response) when expected POST parameters are missing.
Add fallback create function clauses in both files that match any remaining
parameters: in session_controller.ex at line 12, add a create(conn, _params)
clause that displays the flash message "Nome ou senha incorretos." and redirects
to /login; in setup_controller.ex at line 16, add a create(conn, _params) clause
that informs the user that data needs to be reviewed and redirects to /setup.
These catch-all clauses should be placed after the existing pattern-matched
create functions to handle missing or malformed request data gracefully.
In `@lib/taina_web/live/files_live.ex`:
- Around line 306-307: The span element with classes "type-caption text-muted"
contains an inline style attribute `style="align-self: center;"` which violates
coding guidelines that require using utility classes for layout instead of raw
CSS values in HEEX. Remove the inline style attribute and replace it with an
appropriate utility class from the design system (such as a Tailwind utility
class or a BEM component class) that provides the same centering behavior,
ensuring the layout styling is managed through the design system rather than
inline styles.
In `@lib/taina/maraca.ex`:
- Around line 374-390: The request_access function validates the zelador's role
and existing access permissions, but does not explicitly verify that the owner
belongs to the same Tekoa (community) as the zelador before creating the
AccessRequest. Add an explicit guard condition in the cond block to check that
owner.tekoa_id equals zelador.tekoa_id, returning an appropriate error (such as
:invalid_owner_community) if they don't match. This validation must be inserted
before the final true clause that constructs the attrs map and creates the
AccessRequest, ensuring community isolation is enforced as a hard boundary
rather than relying solely on downstream RLS.
In `@lib/taina/maraca/ava.ex`:
- Around line 136-145: The changeset/2 function includes activated_at and
public_id in the cast call, but these are programmatically generated fields that
should not be exposed to user input. Remove activated_at and public_id from the
list of fields passed to the cast function—keep only [:username, :display_name,
:role, :tekoa_id]—since public_id is autogenerated and activated_at is only set
internally via put_change in accept_invite_changeset, not from user input.
In `@lib/taina/maraca/behaviour.ex`:
- Around line 35-42: The second iex example for the invite_user callback
documents a function call with only two arguments, but the callback definition
specifies invite_user/3, which requires three arguments including a keyword
options parameter. Update the second example that calls invite_user(morador,
tekoa) to include the third argument by either passing an empty list [] or a
meaningful keyword option to match the callback's arity of 3 and maintain
consistency with the documented contract.
In `@lib/taina/ybira/behaviour.ex`:
- Around line 69-72: The callback documentation for restore_file and other file
operations in lib/taina/ybira/behaviour.ex incorrectly claims "Dono ou zelador"
(Owner or caretaker) permissions, but the implementation enforces owner-only
access. Update the documentation strings for all affected callbacks to
accurately reflect owner-only permissions at lines 69-72 (restore_file), 79-82,
87-89, 106-108, 113-115, and 121-123 to state "apenas dono" (owner only) instead
of including caretaker access. Align the authorization documentation with the
actual implementation to prevent permission confusion and misinterpretation.
In `@priv/repo/seeds.exs`:
- Around line 15-17: The seeds.exs file is missing the required `import
Ecto.Query` statement. Add `import Ecto.Query` at the top of the file alongside
the existing alias statements (Taina.Maraca, Taina.Scope, Taina.Ybira) to follow
the repository's coding conventions for seed files.
In `@test/taina/maraca_test.exs`:
- Around line 103-106: The Repo.update! calls at lines 103-106 and at lines
244-247 in test/taina/maraca_test.exs are executing outside of Repo.with_tekoa/2
context, which violates explicit RLS boundaries required by the coding
guidelines. Wrap both Repo.update! operations inside Repo.with_tekoa/2 blocks to
ensure Row-Level Security isolation is maintained and the test behavior remains
robust when RLS enforcement is strict. This applies to both the eight_days_ago
token expiration setup and any other similar test data modifications.
---
Outside diff comments:
In `@lib/taina_web/controllers/setup_controller.ex`:
- Around line 46-52: The current code in the setup_controller.ex file allocates
the entire available disk space to storage_quota_bytes, which leaves no margin
for database, logs, and system operations. In the pattern match where kbytes is
successfully parsed and the function returns kbytes * 1024, apply a reserve
buffer (a fixed percentage or absolute value) to deduct from the available space
before calculating the storage quota, ensuring that a portion of disk space is
preserved for system needs rather than being fully allocated to user uploads.
- Around line 12-13: Update the domain language in the comment at
setup_controller.ex lines 12-13 where it currently refers to "admin reajusta
depois" by replacing the word "admin" with "zelador" to align with the coding
guidelines and the current domain terminology for administrative users in this
system.
In `@lib/taina_web/live/file_preview_live.ex`:
- Around line 14-36: Extract the case/match decision logic from the mount/3
callback into a pure helper function. The helper should accept the file lookup
result and return either {:ok, state_map} or {:error, error_type} to represent
the three branches (successful load, not_found error, and generic error). Then
refactor mount/3 to call this helper and use its result only for orchestrating
assign, put_flash, and redirect operations. This separates the decision-making
logic (which belongs in the helper and is easily testable) from the
state-mutation operations (which stay in the callback).
In `@lib/taina_web/live/gallery_live.ex`:
- Around line 49-60: The pattern matching using {:ok, ...} = on the results from
Jaci.list_photos and Jaci.timeline calls will crash the LiveView if these
functions return an error tuple. Create helper functions that handle both
success and error cases from Jaci function calls, returning either the expected
data on success or safe default values (empty lists/cursors) along with a flash
error message on failure. Replace the direct pattern matching calls in the
load_photos function and the ensure_timeline function with calls to these new
helpers that gracefully handle errors without crashing the gallery.
In `@lib/taina_web/live/login_live.ex`:
- Around line 96-101: The modal text in the invite-help modal uses "Quem
administra a comunidade" which is inconsistent with the product's vocabulary
guidelines that favor caretaker language. Replace "administra" with "cuida" in
the gettext string within the modal with id "invite-help" so it reads "Quem
cuida da comunidade te manda..." to maintain consistency with the rest of the
user flow and align with the zelador/caretaker vocabulary standard.
In `@lib/taina/maraca/access_request.ex`:
- Around line 43-46: Standardize the domain terminology throughout the
access_request.ex module by replacing all references to admin/administrador with
zelador and member with morador. This includes updating the iex example that
shows admin soliciting access (lines 43-46 anchor location), and also update the
related example sections at lines 72-73, 117-118, and 141-142 to consistently
use the zelador and morador terminology instead of the admin/administrador
nomenclature. Review the moduledoc and all code examples to ensure the domain
language aligns with the current social/technical contract.
In `@lib/taina/maraca/behaviour.ex`:
- Around line 78-109: The authenticate/3 callback in the behaviour module still
documents and types the old email-based authentication flow with confirmed_at
checks, but SessionController is calling it with username-based authentication
that uses activated_at instead. Update the callback specification to reflect the
actual username-based flow: change the first parameter documentation from email
to username, update the business rules section to reference activated_at instead
of confirmed_at, replace the :email_not_confirmed error atom with the
appropriate activation error (such as :user_not_activated), and update the
example calls to use username values instead of email addresses. This will align
the contract with how SessionController actually invokes Maraca.authenticate.
- Around line 159-168: Update the docstrings and comments in
lib/taina/maraca/behaviour.ex to maintain consistent domain vocabulary by
replacing all instances of admin-related terms with zelador-related terms. At
lines 159-168 (anchor location), replace references to "admin" with "zelador",
"admin_scope" with "zelador_scope", and "admins primeiro" with "zeladores
primeiro"; additionally update any instances of "contas não confirmadas" to
"convites pendentes". Apply the same terminology replacements at the sibling
locations: lines 220-238, 327-352, 520-555, and 641-643 to ensure consistency
throughout the public contract and documentation. This aligns the code with
coding guidelines requiring `:zelador` usage instead of admin terminology.
In `@lib/taina/ybira.ex`:
- Around line 292-296: The `list_folder_contents` function extracts `:limit` and
`:offset` from options without validating them, which can cause pagination
issues when limit is zero or negative, leading to cursor progression problems.
Normalize both the `limit` parameter to ensure it's a positive integer (using a
minimum value if needed) and the `offset` parameter to ensure it's a
non-negative integer before using them in pagination logic. Apply the same
validation pattern to all other pagination methods in the codebase that extract
these parameters from options.
In `@test/taina_web/controllers/session_controller_test.exs`:
- Line 2: The session_controller_test.exs module uses `async: true` in its `use
TainaWeb.ConnCase` statement, but the module creates Tekoa during setup. Since
the test suite enforces a single_tekoa_enforcement constraint that prevents
multiple Tekoa instances from running in parallel, running this test module
concurrently with other test modules that also create Tekoa will cause
intermittent failures. Change `async: true` to `async: false` in the use
statement to ensure this module runs synchronously and avoids conflicts with
other tests.
---
Duplicate comments:
In `@lib/taina_web/live/storage_live.ex`:
- Around line 26-34: The load_stats/1 function uses strict pattern matching with
{:ok, ...} on the return values of Ybira.storage_stats(scope) and
Ybira.storage_stats_by_kind(scope), which will cause the LiveView to crash if
either function returns an error. Replace the strict pattern matches with
conditional logic (such as case statements or with patterns) that handles both
success and error cases. When an error occurs, assign appropriate default or
error state values to the socket instead of allowing the match to fail, ensuring
the LiveView degrades gracefully rather than crashing.
In `@lib/taina_web/router.ex`:
- Line 64: The pipe_through directive at line 64 references a pipeline called
`:require_authenticated` that is not defined in the router. Either define this
pipeline in the router scope to handle authentication requirements for the
routes that follow it, or replace `:require_authenticated` with an existing
pipeline name that is already defined in this router file. Ensure the pipeline
name matches an actual pipeline definition to properly chain the authenticated
routes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f069c636-71cf-4855-b573-5edeb07c3847
📒 Files selected for processing (60)
.dialyzerignore.exsassets/css/components.cssassets/css/layouts.cssassets/css/utilities.cssassets/js/app.jsassets/js/hooks.jslib/taina/jaci/behaviour.exlib/taina/maraca.exlib/taina/maraca/README.mdlib/taina/maraca/access_request.exlib/taina/maraca/ava.exlib/taina/maraca/behaviour.exlib/taina/ybira.exlib/taina/ybira/behaviour.exlib/taina_web/components/core_components.exlib/taina_web/components/icons.exlib/taina_web/components/layouts.exlib/taina_web/components/layouts/root.html.heexlib/taina_web/controllers/invite_controller.exlib/taina_web/controllers/password_controller.exlib/taina_web/controllers/session_controller.exlib/taina_web/controllers/setup_controller.exlib/taina_web/hooks.exlib/taina_web/live/account_live.exlib/taina_web/live/file_preview_live.exlib/taina_web/live/files_live.exlib/taina_web/live/gallery_live.exlib/taina_web/live/home_live.exlib/taina_web/live/invite_accept_live.exlib/taina_web/live/invite_live.exlib/taina_web/live/login_live.exlib/taina_web/live/members_live.exlib/taina_web/live/reset_password_live.exlib/taina_web/live/setup_live.exlib/taina_web/live/storage_live.exlib/taina_web/live/upload_live.exlib/taina_web/router.exmix.exspriv/gettext/default.potpriv/gettext/pt_BR/LC_MESSAGES/default.popriv/repo/migrations/20260616120000_identity_username_first.exspriv/repo/seeds.exstest/support/fixtures.extest/taina/jaci_test.exstest/taina/maraca_members_test.exstest/taina/maraca_test.exstest/taina/rls_isolation_test.exstest/taina/ybira_stats_test.exstest/taina_web/controllers/session_controller_test.exstest/taina_web/controllers/setup_controller_test.exstest/taina_web/live/files_live_test.exstest/taina_web/live/gallery_live_test.exstest/taina_web/live/home_live_test.exstest/taina_web/live/invite_flow_test.exstest/taina_web/live/login_live_test.exstest/taina_web/live/members_live_test.exstest/taina_web/live/setup_live_test.exstest/taina_web/live/storage_live_test.exstest/taina_web/live/trash_live_test.exstest/taina_web/live/upload_live_test.exs
Correctness:
- router: drop undefined :require_authenticated pipeline (on_mount authenticates)
- home/storage/files LiveViews: handle {:error,_} from Ybira (flash + empty
state) instead of crashing on strict matches
- session/setup controllers: fallback create/2 for malformed POSTs
- maraca.request_access: guard owner/zelador cross-tekoa (:cross_tekoa_owner)
- Ava.changeset/2: drop programmatic :activated_at/:public_id from cast
Docs/governance:
- ybira behaviour callbacks: owner-only (aligns code + RFC 003)
- maraca behaviour/permission/access_request + ybira comment: admin -> zelador
- maraca behaviour: fix invite_user/3 example arity
UI/design system:
- remove auth-layout footer (component + dead CSS)
- replace all inline HEEx styles with token-backed utilities/classes
(measure scale, self-center, contents, media-frame, doc-frame)
- raw rgb() -> --field-bg; color-scheme to base.css; translateY(8px) -> space-2
Tests/seeds:
- seeds.exs: try/after to clean tmp dir on failure
- maraca_test: wrap Repo.update! in Repo.with_tekoa for RLS boundary
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
assets/css/components.css (1)
227-229:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFoco do campo de busca fica invisível para teclado.
Na Line 227 e Line 228,
outline: noneremove a pista visual de foco doinputda busca sem um estado substituto. Você considera aplicar:focus-visibleno.searchou noinputcom borda/outline tokenizado, para manter navegação por teclado clara?Patch sugerido
-.search input:focus { - outline: none; -} +.search:focus-within { + outline: var(--border-thick) solid var(--brand-primary); + outline-offset: 2px; +} + +.search input:focus { + outline: none; +}As per coding guidelines, "Ensure visible focus states" e "Design for the least-technical person in the community, not power users".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@assets/css/components.css` around lines 227 - 229, The `.search input:focus` rule removes the outline without providing a replacement visual indicator, making the search field focus state invisible for keyboard users. Replace the `outline: none` declaration in the `.search input:focus` selector with a visible focus indicator—either restore the outline using a tokenized color value or add a tokenized border. Alternatively, use `:focus-visible` instead of `:focus` to ensure the focus indicator only appears for keyboard navigation, improving accessibility while maintaining design intent.Sources: Coding guidelines, Learnings
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@assets/css/components.css`:
- Around line 823-833: The `.media-frame` and `.doc-frame` classes use raw
hardcoded values (280px and 70dvh respectively) instead of semantic tokens,
violating the tokenized component contract. Create semantic token variables in
assets/css/tokens.css for these measurements (such as --preview-media-min-height
and --preview-doc-height), then update the min-height property in .media-frame
and the height property in .doc-frame to reference these tokens using the var()
function instead of the raw values.
---
Outside diff comments:
In `@assets/css/components.css`:
- Around line 227-229: The `.search input:focus` rule removes the outline
without providing a replacement visual indicator, making the search field focus
state invisible for keyboard users. Replace the `outline: none` declaration in
the `.search input:focus` selector with a visible focus indicator—either restore
the outline using a tokenized color value or add a tokenized border.
Alternatively, use `:focus-visible` instead of `:focus` to ensure the focus
indicator only appears for keyboard navigation, improving accessibility while
maintaining design intent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 329431cf-0a50-47b4-b981-fc7e0b3db40c
📒 Files selected for processing (28)
assets/css/base.cssassets/css/components.cssassets/css/layouts.cssassets/css/tokens.cssassets/css/utilities.csslib/taina/maraca.exlib/taina/maraca/access_request.exlib/taina/maraca/ava.exlib/taina/maraca/behaviour.exlib/taina/maraca/permission.exlib/taina/ybira.exlib/taina/ybira/behaviour.exlib/taina_web/components/layouts.exlib/taina_web/components/layouts/root.html.heexlib/taina_web/controllers/session_controller.exlib/taina_web/controllers/setup_controller.exlib/taina_web/live/account_live.exlib/taina_web/live/file_preview_live.exlib/taina_web/live/files_live.exlib/taina_web/live/home_live.exlib/taina_web/live/invite_live.exlib/taina_web/live/members_live.exlib/taina_web/live/storage_live.exlib/taina_web/live/trash_live.exlib/taina_web/live/upload_live.exlib/taina_web/router.expriv/repo/seeds.exstest/taina/maraca_test.exs
Reconcile the Fase-1 holes + Nhaman backup work onto main's current Maraca model (LiveView frontend, #159), which diverged after this branch's base. Conflict resolution + adaptation: - Roles: :admin/:member -> :zelador/:morador; :last_admin -> :last_zelador. - Auth: keep main's username-first authenticate/3; wrap it with the login rate limit (keyed by tekoa+username, no email anywhere). - Drop branch's duplicate list_members and its log_in_ava/renew_session in auth.ex; main already provides list_members and session rotation (log_in/2). - Keep branch's net-new work, adapted: account deactivation (deactivated_at, :account_deactivated gate), member role mgmt, Hammer rate limit, Nhaman backup (pg_dump + erl_tar) with the taina.backup.verify restore drill. - config/mix/lock unioned (Hammer + backup cron alongside main's web deps). Member management is now wired into MembersLive (role change + deactivate/ reactivate, zelador-guarded, last-zelador protection), with tests. Full suite green (170 tests).
Problema
Faltava uma interface web completa (frontend) para suportar os fluxos principais do sistema (Maraca: setup/login/convites/reset; Ybira: arquivos/armazenamento/upload/lixeira; Jaci: galeria de fotos/vídeos), além de inexistirem telas e componentes do design system para navegar e operar esses recursos.
Solução
Implementação de um frontend em Phoenix LiveView com:
LiveSockete hooks (copy to clipboard, navegação do viewer com teclado/swipe, DnD com highlight de dropzone, share com fallback, auto-dismiss de flash) e topbar de progresso.Explicação
A implementação foi feita para criar uma base consistente e escalável: tokens e componentes tokenizados como “fonte de verdade” para UI, responsividade mobile-first, integração clara entre templates HEEx/layouts e callbacks do LiveView (lógica no servidor e interações no browser via hooks), além de acessibilidade/motion e internacionalização para manter textos e estados coerentes ao longo das telas.