Skip to content

Latest commit

 

History

History
98 lines (85 loc) · 12.3 KB

File metadata and controls

98 lines (85 loc) · 12.3 KB

PROJECT MEMORY & DECISION LOG

This file contains lessons learned, architectural decisions, and specific workarounds found during development. AGENTS: Read this before starting any task to avoid repeating past mistakes.

[2025-12-06] Future Feature: Purchase Item Cost Alert

  • Context: While removing 'cost_price' from the direct product form, the user requested a future feature related to validating selling price against purchase item costs.
  • Requirement: Add a "text alert" if harga_jual * qty > purchase_items. This implies a need to check if the total selling value (selling price multiplied by quantity) of derived products exceeds the original cost recorded in a PurchaseItem.
  • Note: This feature needs to be implemented on a form or view that allows for input of quantity and displays selling price in relation to available purchase items and their costs.

[2025-12-06] CRITICAL: Project uses reka-ui for Shadcn-Vue

  • Context: Standard shadcn-vue components were implemented using radix-vue, which broke existing components (Button) and caused errors in new ones (Calendar). This was based on a misunderstanding of the project's setup.
  • Problem Analysis: This project uses a customized shadcn-vue setup that is built upon reka-ui, not the more common radix-vue. Replacing components with radix-vue versions is a breaking change. Components must be used according to the reka-ui API.
  • Solution:
    1. All core components (Button.vue, Calendar.vue, etc.) were reverted to their original reka-ui implementations.
    2. The Calendar component error (defaultValue.copy is not a function) was fixed by providing it the expected date object. This involves importing today and getLocalTimeZone from @internationalized/date, initializing the form v-model with today(getLocalTimeZone()), and transforming the value back to a string (.toString()) on form submission.
  • Rule: DO NOT replace reka-ui based components with radix-vue versions. This project's shadcn-vue implementation is customized. When working with components like Calendar, use date objects from @internationalized/date to ensure compatibility.

[2025-12-06] Inertia, TanStack Table Reactivity, and preserveState Challenges

  • Context: A persistent bug caused @tanstack/vue-table to fail to refresh its data visually after filtering (using router.get with preserveState: true) or after data-mutating actions (router.delete, router.post) after a hard page refresh, despite props updates and table.reset() calls. This issue did not occur during hot-reloading (HMR).
  • Problem Analysis:
    • Inertia's preserveState: true (used for filtering to maintain input focus) prevents component re-mounts. This, combined with the build process, somehow led to a subtle reactivity detachment in the @tanstack/vue-table instance.
    • Even forcing new array references ([...newData]) for the table data did not resolve the issue, indicating that table.reset() or the table's internal reactivity mechanism was failing to re-render in this specific preserveState context after a hard refresh.
    • preserveState: true is the default for POST, PUT, PATCH, DELETE requests in Inertia, which also caused issues for data-mutating actions until explicitly set to false.
  • Solution(s):
    1. For Data-Mutating Actions (DELETE, POST, PUT, PATCH): Explicitly set preserveState: false in the router call options (e.g., router.delete(..., { preserveState: false })). This forces a full component re-render and re-initialization, reliably updating the table.
    2. For preserveState: true Visits (e.g., Filtering): When preserveState: true is required (e.g., for search inputs to maintain focus), and the table fails to refresh:
      • Implement the v-if re-rendering trick:
        • Create a ref (e.g., isTableVisible = ref(true)).
        • Wrap the entire table component in the template with v-if="isTableVisible".
        • In a watcher that detects prop changes, set isTableVisible.value = false, use await nextTick(), then set isTableVisible.value = true. This forces Vue to destroy and re-mount the table component from scratch, ensuring it initializes with the latest data.
  • Rule: When encountering persistent table refresh issues with @tanstack/vue-table and Inertia, particularly with preserveState: true after a hard refresh:
    • Explicitly manage preserveState for router calls: false for actions requiring full re-render, true for state-preserving actions like filtering.
    • If preserveState: true causes issues, implement the v-if re-rendering trick to force a full re-mount of the table component.

[2025-12-06] Admin UI & Data Table Standard

  • Context: A new module was created with an index page that was stylistically and functionally inconsistent with the rest of the admin panel. Several iterations were required to align it.
  • Solution: The PurchaseItems module was refactored to match the conventions found in Products/Index.vue. This established a clear standard for all admin CRUD interfaces.
  • Rules:
    1. Layout: All admin pages must use AuthenticatedLayout.vue. The page title must be placed in the #header slot. The main content must be wrapped in a <div class="tw-bg-white tw-rounded-md tw-p-4">.
    2. Data Tables: All data tables must be implemented using @tanstack/vue-table. Columns should be defined programmatically in the <script> section. The table itself should be rendered using <FlexRender>.
    3. Actions: Row actions must be placed in a dropdown menu. For maintainability, this dropdown should be a dedicated component (e.g., DropdownAction.vue) and rendered in the table cell using Vue's h() function.
    4. Pagination: Use simple "Previous" and "Next" buttons with links provided by Inertia's paginator, not a full list of page numbers.
    5. Styling: Follow all project-specific styling rules, including the tw- prefix for Tailwind classes and using component props (e.g., size="icon") over manual styling where possible.
    6. Localization: All user-facing text in UI components, including labels, buttons, placeholders, and common navigation terms, MUST be in Indonesian.

[2025-12-06] Soft Deletes Implementation

  • Context: Implementing soft delete functionality for a model involves changes across the database, model, controller, routes, and frontend UI.
  • Solution:
    • A migration was created to add a deleted_at column to the purchase_items table.
    • The PurchaseItem model was updated to use the SoftDeletes trait.
    • The PurchaseItemController was updated to:
      • Modify the index method to filter for active, trashed, or all items based on a trashed query parameter.
      • Change resource methods (show, edit, update, destroy) to use withTrashed()->findOrFail($id) for consistent handling of soft-deleted items.
      • Add restore and forceDelete methods.
    • New routes were added for restore (POST) and forceDelete (DELETE).
    • The Index.vue page was updated with a filter (dropdown) to view trashed items.
    • The DropdownAction.vue component was updated to conditionally show "Restore" and "Force Delete" actions for soft-deleted items.
  • Rule: When implementing soft deletes for a model, ensure all layers (database, model, controller, routes, and frontend) are updated to correctly support the lifecycle of soft-deleted items, including filtering, restoring, and permanent deletion.

[2025-12-06] Case-Insensitive Search with ILIKE

  • Context: In PostgreSQL, the LIKE operator is case-sensitive, leading to incomplete search results when users expect case-insensitive matching.
  • Solution: For case-insensitive string searches on PostgreSQL, use the ILIKE operator instead of LIKE in SQL queries.
  • Rule: When implementing case-insensitive string searches in Laravel for PostgreSQL databases, use where('column', 'ilike', '%search%').

[2025-12-06] Preventing bigint SQL Error on Numeric Search

  • Context: A dynamic search query was attempting to compare a bigint column (id) with a non-numeric string, resulting in a invalid input syntax for type bigint SQL error.
  • Solution: Before adding an orWhere clause for a numeric column in a dynamic search, explicitly check if the search term is numeric using is_numeric($search).
  • Rule: Always validate the type of dynamic search parameters against the expected column type (e.g., is_numeric() for integer/bigint columns) to prevent SQL syntax errors.

[2025-12-06] Data Table Reactivity with @tanstack/vue-table

  • Context: After performing actions (delete, restore), the @tanstack/vue-table was not automatically updating despite the underlying data prop changing via Inertia.
  • Solution: While tableData is a computed property, directly watching props.purchaseItems with deep: true and calling table.reset() on change proved to be the most reliable way to trigger a re-render of the @tanstack/vue-table when data changes.
  • Rule: For @tanstack/vue-table implementations with Inertia, use watch(() => props.dataProperty, () => table.reset(), { deep: true }) to ensure reliable table updates when the Inertia prop changes.

[2025-12-06] Incorrect Admin Layout Component

  • Context: When creating new Admin Vue pages, the initial assumption was to use AdminLayout.vue for the layout component. This led to a build error as no such component existed.
  • Solution: Examination of existing Admin pages (e.g., Admin/Dashboard.vue) revealed that AuthenticatedLayout.vue is the correct component to use for Admin pages, imported from @/Layouts/AuthenticatedLayout.vue.
  • Rule: For new Admin Vue pages, always use AuthenticatedLayout.vue as the main layout component.

[2025-12-06] Incorrect Shadcn UI Components Import Path

  • Context: When importing Shadcn UI components in Vue files, the path alias @/components/ui/... was used, leading to a build error.
  • Solution: Investigation of the project structure revealed that Shadcn UI components are located under resources/js/components-shadcn/ui/. The correct import path uses @/components-shadcn/ui/....
  • Rule: When importing Shadcn UI components, use the path alias @/components-shadcn/ui/.

[2025-12-06] Tailwind CSS Prefix Convention

  • Context: Vue components were created using standard Tailwind CSS utility classes (e.g., flex, items-center) which was incorrect. The initial correction to -tw- was also wrong.
  • Solution: The project convention requires all Tailwind CSS classes to be prefixed with tw- (e.g., tw-flex, tw-items-center). All newly created Vue components were updated to follow this rule.
  • Rule: When writing or editing Vue components, all Tailwind CSS classes MUST be prefixed with tw-.

[2025-12-06] Shadcn UI Button Sizing and Padding

  • Context: When using Shadcn UI's Button component, explicitly applying Tailwind CSS classes like tw-w-8 tw-h-8 tw-p-0 can interfere with the component's internal styling, especially for icon-only buttons. This caused content (like the MoreHorizontal icon) to be clipped or not rendered correctly.
  • Solution: For icon-only buttons, leverage the size="icon" prop provided by the Shadcn UI Button component. This prop automatically applies the correct width, height, and padding for icon buttons, ensuring content is displayed properly without manual class overrides.
  • Rule: When styling Shadcn UI Button components, especially for icon buttons, prefer using the size prop (e.g., size="icon") over explicit tw-w-*, tw-h-*, and tw-p-* classes to avoid conflicts and ensure proper rendering.

[2025-12-06] Localization to Indonesian

  • Context: There was confusion about whether certain common English terms ("Filter", "Next", "Previous") should be localized. The initial implementation was fully Indonesian.
  • Solution: The final decision is to localize all user-facing text to Indonesian, including common terms and placeholders, for consistency across the application. The components have been updated to reflect this.
  • Rule: All user-facing text in UI components, including labels, buttons, placeholders, and common navigation terms, MUST be in Indonesian.