feat(kinetik): sync kipApp org structure (teams/projects/members/employees)#81
Open
ryanaidilp wants to merge 58 commits into
Open
feat(kinetik): sync kipApp org structure (teams/projects/members/employees)#81ryanaidilp wants to merge 58 commits into
ryanaidilp wants to merge 58 commits into
Conversation
…tch 2) Replace raw <table> markup with shadcn-vue Table/TableHeader/TableBody/ TableRow/TableHead/TableCell across 6 pages (Laporan/Pegawai, Projects/Index, Matrix/Index, Performance/ProjectDetail, and the Kinetik TeamWeeklyRecap + WeeklyScrapper recaps). Load-bearing layout classes preserved (Matrix sticky frozen header/first-column grid; per-cell alignment/width/responsive-hide classes). Buttons intentionally NOT blanket-converted: every remaining raw <button> is a deliberate non-standard control (segmented/tab toggles, icon-only nav/delete, or text-link actions) — standard actions already use the shadcn Button. Converting them to <Button> would regress the UI.
Fix the two Pint --test violations blocking CI Lint PHP on PR #76: - KipIntegrationControllerTest: import ConfigBearerAuthenticator + Http - routes/web.php: alphabetize KipIntegrationController import
refactor(ui): shadcn-vue audit batch 2 — tables (+ button audit)
…oyees) Mirror kipApp organisational structure into local master tables: - KipStructureSource contract + ApiKipStructureSource (monitoring/hirarki/daerave directory for all unit teams, proyek?timkerjaid for projects+members, timkerja/anggota for team roster) + MockKipStructureSource - SyncKipStructureAction: upsert teams/projects by kip_external_id, provision employees by nip_lama (set home team + leader), mirror project_members, additive employee_team pivot - kinetik:sync-kip-structure command (weekly schedule) + admin UI button - token expiry 'expiring soon' detection + badge; 2-column integration layout - show kipApp NIP + team on employees list - add kip_external_id to teams/projects Tests: 18 new (DTO/source/action/command), all green.
…ployee logins The synchronous sync exceeded PHP max_execution_time (30s) -> 500. The server has no queue worker, so sync is now driven client-side one team per request: - kip_sync_runs table + model track total/processed/summary for resume - SyncKipStructureAction::syncTeam() syncs a single team; execute() loops it - controller step endpoint processes one team per POST; index exposes the run - KipIntegration.vue shows a determinate progress bar; auto-resumes a running run on (re)load - business logic moved into a Pinia store (stores/kipSync.ts); interfaces moved to resources/js/types; component is a thin consumer - provision login Users for employees (email derived firstname@bpssulteng.id, password 'password', staff role) Verified live: 25 teams / 165 projects / 108 employees / 108 logins, progress 4/25 -> done, no timeout. 71 tests green; pint + vue-tsc clean.
- Activity sync (syncAll) was still synchronous -> 500 at 30s for 108 employees. Now chunked like structure: a batch of employees per request (KIP_ACTIVITY_CHUNK, default 5), tracked in kip_sync_runs (type=activities), with a progress bar and resume; kipSync store gains startActivitySync(). - New admin Kegiatan kipApp list page (/kegiatan-kipapp): paginated kip_activities with employee, date, uraian, RK, progress, claimed status, evidence; search + status filter. Sidebar nav entry (admin). Verified live: activity sync 35/108 -> progressing, no timeout. 76 tests green; pint + vue-tsc clean.
The kegiatan list was admin-only, so regular users had nowhere to see their synced activities (Rekap Mingguan is week-scoped). Now /kegiatan-kipapp is open to any authenticated user: admins (manage-kip-integration) see all activities, everyone else is scoped to their own linked employee (all dates). Page hides the Pegawai column + retitles to 'Kegiatan Saya' for own scope; sidebar shows the entry to staff too. Verified live as a staff user. 5 list tests green; pint + vue-tsc clean.
Synced employees who were project members but not in the team roster ended up with a home team_id but no employee_team pivot row (assignHomeTeam used updateExistingPivot, a no-op when no row exists). Team-scoped views (Rekap Mingguan/Tim/Bulanan/Triwulanan) resolve membership via employee_team, so those users saw 'not joining any team'. Now everyone working in a team (leader + project members + roster) gets an employee_team row; assignHomeTeam uses syncWithoutDetaching to create the home row when missing. Verified: Sukma now resolves her 4 real teams.
Rekap Mingguan defaulted to the current week, so staff whose only synced activities are older saw nothing to claim. It now anchors on the week of the employee's most recent activity (falling back to the current week); the ?week navigator still overrides.
…r scoping) Per RFC, bukti rapat (Notula/Foto/Daftar Hadir) and recap paraphrase are PJ/Ketua-Tim-only, but the controller authorized any team member. Now storeEvidence/destroyEvidence/storeOverride require the employee to lead the team (teams.leader_id or 'leader' pivot role); recap pages receive a canManage flag and hide the Tambah Bukti / Parafrase / delete controls for non-PJ. 14 tests green (PJ allowed, member 403, canManage true/false).
- IKU (performance_indicators): synced from each project's team-leader RK
(rkketuaid/rencanakinerjaketua) during the structure sync; projects link to it.
- RK (performance_plans): kipApp RKs are team+employee scoped (rkid -> timkerjaid),
so plans gain kip_external_id + team_id and project_id becomes nullable.
* Backfill: BackfillRkAction derives claimable RKs from synced activities
(rk_external_id/rk_name) so every activity has an RK; runs inside the
activity sync.
* Cascade: SyncKipPlansAction + kinetik:sync-kip-plans (scheduled) pull skp/rk
+ skp/iki per employee and set the authoritative team + numeric target parsed
from the IKI text (KipRkData::parseTarget).
- WeeklyActivity claim RK list now includes team-level RKs.
- Fix dashboard team-ranking x-axis label (was showing synthetic KIP-xxxxx code;
now a readable short name/acronym).
13 new tests; full Kinetik suite green; pint + vue-tsc clean.
…t + ketua role; add RFC use-case docs - PerformancePlanController/PerformanceIndicatorController: non-admins now see RK and IKU across ALL their teams (member or leader) incl. team-scoped RKs, not just their home team (was empty for members / single-team only). - Sidebar footer shows the employee position (jabatan) instead of the role. - Structure sync records the team leader as project_members role=leader so the dashboard 'Top 10 as Ketua' ranking is non-zero (needs re-sync to apply). - Dashboard 'Capaian per Tim' chart is horizontally scrollable with a readable short label (acronym/truncated name) instead of the synthetic KIP-xxxxx code. - Add top spacing before the personal-projects section for non-lead staff. - docs/kinetik: RFC-002/003, actors, use-cases (+ diagram PNG) and DOCX exports.
rfc-002/003 DOCX previously had mermaid as literal code blocks; now each diagram is pre-rendered (mmdc) and embedded as an image before the pandoc conversion.
Input Kinerja (Domain A monthly per-butir input) is replaced by the kipApp claim flow for staff; keep it for head/pimpinan.
…ly Solusi/RTL - Remove the 'Tambah Kegiatan Manual' feature (route + controller + UI); claims only come from synced kipApp activities. - Kendala (obstacle) is now required on a claim. - Solusi + Rencana Tindak Lanjut are shown and saved only for a PJ (team leader); for regular members they're hidden and stripped server-side (filled later at the team recap).
…ext columns - SaveActivityClaimAction authorized by the plan's project team and threw 'RK has no project' for kipApp RKs (which are team-scoped, project_id null, validated against the live skp/rk API). Now authorizes by the plan's team (project team -> fallback to plan team_id). Verified end-to-end. - Widen kip_activities.evidence_url + rk_name (and activity_claims.evidence_url) to text: kipApp datadukung URLs / rencanakinerja exceed varchar(255) and overflow on PostgreSQL (prod).
- Remove the 'Sinkronkan kipApp' button on Rekap Mingguan, the weekly.sync route, controller method, and its tests; per-user manual sync is no longer needed (admin centralized sync + cron handle it). - DatabaseSeeder now seeds only roles/permissions + the admin user; teams, employees, projects, IKU, RK and logins come from the kipApp sync.
Discover each employee's skpid from /dashboard/rkpegawai (state-independent)
unioned with belumkirim, and stop filtering on sentAt, so already-submitted
activities are imported. Normalise rkpegawai's object-vs-list response shape
({jumlahrk:0}) to avoid a TypeError. Rename fetchUnsentActivities -> fetchActivities.
Dedup IKU by (team_id, name) and RK by (team_id, description) so projects and members sharing the same text no longer create duplicate rows. When a team's niplamaketua is absent from any roster, provision the leader so every team gets a leader_id.
…ode column Resolve the plan team via project?.team_id ?? team_id so editing a team-scoped RK (no project) no longer 500s. Scope a staff member's RK list to plans they own or teams they lead. Expose per-row can_update/can_delete and hide edit/delete/ create controls when not permitted. Remove the always-empty Kode column from the RK/IKU tables and forms (kipApp has no human code).
Match week_start with whereDate (the date cast stores Y-m-d H:i:s, so plain equality on a date string never matched). Collapse a claimed activity's form to its header with an Ubah toggle so it is not duplicated against Rekap Tersimpan. Hide the empty project name in the RK picker for team-scoped RKs.
…aults The recap aggregator only matched claims whose RK had a project, so team-scoped RKs (project_id null) never appeared in any team recap. Match by the plan's team_id OR its project's team_id. Default the team-recap view to a team the user leads and anchor the period to the latest claim, so a PJ sees member claims without manually switching team/week.
kipApp's API exposes no employee email, so logins were derived as firstname@bpssulteng.id. Seed a niplama->SSO username map (database/data) and, during structure sync, match by niplama to set the real email username@bps.go.id, upgrading existing derived logins on re-sync and falling back to derived when a niplama is unmapped or the email collides.
Turn the weekly/monthly/quarterly team recaps into a PJ review screen: read-only color-coded Capaian sortable to surface low achievers, a per-RK Konfirmasi toggle (confirmed_at/confirmed_by), and an expand-row paraphrase panel writing recap_overrides. Member Solusi/RTL columns removed (members only fill Kendala); weekly gains paraphrase support via week_start on recap_overrides.
… recap The team recap paired the summed claim target with the plan's IKI unit (e.g. 2 with '%'); use the claim's own unit instead (2 Kegiatan). Remove the PJ-only Solusi/RTL columns from the member's Rekap Tersimpan (members only fill Kendala).
Add a 'Perlu perhatian' filter (capaian<100% / has kendala / unconfirmed), a 'Konfirmasi semua 100%' bulk-confirm endpoint + button, and pre-fill the PJ Kendala from the aggregated member text. Lets a PJ confirm the clean majority in one action and only hand-edit the exceptions.
Monthly and quarterly recaps now inherit the PJ's weekly paraphrases (combined over the period) as the pre-fill source — priority pj override > inherited weekly > aggregated member text — with a 'Tarik dari mingguan' pull button. Narrative is written once at the weekly level and rolls up to the FRA.
A plan's pic_employee_id may now store the recap paraphrase (Kendala/Solusi/RTL) for their own RK, so drafting is shared across the team. Confirmation, bulk confirm, and meeting evidence stay PJ-only. Per-row editing is gated on isPj || pic, surfaced via pic_employee_id + currentEmployeeId.
…t-sync timezone - Add skp_status to performance_plans (migration + fillable) — synced from /v1/skp per employee in ApiKipStructureSource via new fetchSkpStatusMap() - KipRkData carries skpStatus; SyncKipPlansAction persists it on upsert - WeeklyActivityController resolves rk_external_id → matched_plan_id so the claim form can auto-select the correct RK without manual pick - SyncKipStructureAction: second-pass leader backfill for teams/projects that had no leader_id because the leader employee didn't exist yet on first pass - KipIntegrationController: wrap last_fetched_at with Carbon::toIso8601String() so the frontend receives a proper UTC-tagged string and displays local time
RK is already assigned from KipApp sync via matched_plan_id, so the combobox picker (and its Check/ChevronsUpDown icons) is replaced with a plain read-only display. Removes Popover/Command components, planPickerOpen state, and the check-indicator icon from the claim form.
- WeeklyActivityController: replace back() with explicit redirect to weekly.index?week=<computed> so Inertia always receives a clean GET response with fresh activities + recap props (back() uses the Referer header which can drop query params and produces stale Inertia state) - WeeklyScrapper: watch props.activities and re-sync claimForms when Inertia refreshes the page so the badge, form visibility, and claim data all reflect the server state without a hard reload
Drop the per-row confirm checkbox, bulk-confirm button, toggleConfirm, bulkConfirmIds, bulkConfirming, and the Konfirmasi column header. Also remove !row.is_confirmed from needsAttention so the attention filter no longer flags unconfirmed rows. Cleans up unused Check icon and computed import.
…ress + refactor TeamWeeklyRecap Dashboard computeTeamProgress() and personalStats() now aggregate from both performance_reports (old system) and activity_claims (Kinetik). Teams/employees using only Kinetik will finally show progress without requiring a hard refresh. TeamWeeklyRecap logic extracted to useTeamWeeklyRecap composable so the .vue file only contains template and a single composable call.
…n + fix Item Kerja Hespri's "Tim yang Saya Pimpin" showed 0/N sudah input for all projects even after team members filed Kinetik claims. Fix: compute kinetik_submitted_count per team from activity_claims and attach it to teamProjects; ledProjectSubmittedCount falls back to it when no old-system performance_reports exist. Also fall back to Kinetik plan count for "Item Kerja" when a user has no work_items (i.e. their projects use performance_plans instead).
teamList previously mapped ALL teams and defaulted avg to 0 for teams with no reports, causing the ranking list to be filled with 0% entries. Now only teams with report_count > 0 appear, so the chart and the ranking list stay consistent.
…am rankings - computeTeamProgress now includes activity_claims alongside performance_reports - topEmployeesByAchievement merges Kinetik per-employee avg so Sukma appears at 100% - staffDashboard enriches project cards with team-level kinetik_avg and kinetik_plan_count - DashboardPersonalProjects falls back to kinetik_avg when no performance_reports exist - Revert team list filter — show all teams regardless of report_count
…on projects Replace team-level activity_claims aggregation with per-plan employee-specific cards. Each claimed performance_plan now appears as its own card showing its actual achievement, so one employee's claim no longer inflates all projects in the same team.
…k claims When an employee has activity_claims, suppress old-system project cards that have no performance_reports — they would only show 0% and add noise. Show only: old projects with actual data + the per-plan Kinetik cards. Fall back to all old projects when the employee has no Kinetik claims.
…, remove dead code
Replace performance_reports/work_items system with activity_claims/performance_plans:
- staffDashboard/headDashboard: project cards now come from employee's claimed
performance_plans instead of old Project→WorkItem→PerformanceReport chain
- personalStats: rewritten to use only activity_claims (removes project_members,
work_items, performance_reports queries)
- computeTeamProgress: Kinetik-only (removes old performance_reports branch)
- topEmployeesByAchievement: Kinetik-only (removes old per-project avg branch)
- ProjectWithItems type simplified to {id, team_id, name, team, achievement}
- TeamProjectWithMembers.work_items removed; kinetik_submitted_count retained
- ledProjectSubmittedCount uses kinetik_submitted_count directly
- StaffDashboard: removed kinetikPlanCards prop and allProjects computed
- DashboardPersonalProjects: removed work_items/performance_reports fallback logic
Adds fields referenced by Kinetik recap/integration/activities pages that were absent from index.d.ts, causing vue-tsc compilation failures: - RecapRow: is_confirmed, confirmed_by, pj_*/inherited_* paraphrase fields, pic_employee_id - KipActivity: matched_plan_id - PerformanceIndicator, PerformancePlan: can_update, can_delete - PerformancePlan: team - New: KipSyncRun, KipCredential, KipIntegrationStats, KipActivityRow, Paginated<T>, PaginationLink
…ian attribution
Dashboard:
- computeTeamProgress + topEmployeesByAchievement now use activity_claims (dead performance_reports removed)
- personalStats: shows total team plans count (not just claimed), PJ falls back to team-wide avg achievement
- staffDashboard $projects: PJ with no personal claims sees all team plans instead of empty list
- Added User type hint to staffDashboard/headDashboard (fixes P1132 IDE warning)
Weekly team recap:
- Remove all per-plan PJ editable fields from expanded rows (weekly view is read-only per plan)
- Add single "Ringkasan Mingguan PJ" card (uraian, kendala, solusi, RTL) per team per week
- Pre-fill button formats uraian as numbered list grouped by contributor name
- New weekly_team_notes table + WeeklyTeamNote model + storeWeeklyNote route/controller
- RecapAggregator: uraian_items[] per row carries {name, uraian} for each member claim
- recap_overrides gains uraian column (for monthly/quarterly per-plan paraphrase future use)
Added to EXCLUDES: - *.png / *.jpg / *.jpeg / *.gif — root screenshot artefacts (13+ files) - *.zip — deployment archives (replaces matriks-kinerja_*.zip) - .mcp.json — local MCP config - .playwright-mcp/* — playwright session data - .kipapp/* — kipApp local config - *.postman_collection.json — Postman collections - database/seeders/data/*.prod.json — gitignored prod seed files that may exist on disk
…i/RTL per item
- Add lang/id/validation.php with full Indonesian validation translations
and attribute name mappings (kendala, solusi, RTL, uraian kegiatan, etc.)
- Set APP_LOCALE=id in .env.example
- Fix WeeklyActivityController error message to Indonesian ("Tidak diizinkan:")
- Restore per-plan Solusi (PJ) and RTL (PJ) editable fields in expanded rows
- Remove Kendala from PJ per-plan section (member-only)
- Weekly note form now includes all four fields: Uraian + Kendala + Solusi + RTL
- Pre-fill button includes contributor attribution with numbered list format
…ber data Extends prefillFromMembers (renamed from prefillUraianFromMembers) to also populate the weekly note's obstacle/solution/RTL fields from each plan's aggregated member values, deduplicating across plans.
…or non-managers - Regular members (not admin, not team lead) now only see projects they are actually enrolled in via project_members - Edit and Delete buttons (and the Aksi column header) are hidden when the user lacks create permission (canCreate = false for plain members)
- ProjectController: members no longer have team_id auto-scoped so cross-team project memberships are shown (was cutting Sukma from 18→14) - ProjectController: $teams dropdown for members now covers all teams their projects span, not just their primary team - DashboardController personalStats(): projects_count now counts actual project_members rows for the year, not performance_plans in the team (was showing 7 plans instead of 18 real projects for Sukma)
- Tim Kerja → Rekap Tim (weekly) - Proyek → Halaman Proyek - Item Kerja → Input Kegiatan Mingguan - Rata-rata Capaian → Rekap Tim (weekly) Cards gain hover:shadow-md and label color transition on hover.
Was hardcoded to 1 if employee had any team_id. Now counts distinct teams via project_members for the current year, so cross-team members (e.g. Sukma in 4 teams) see the correct number.
Creates 853 activity_claims across 14 teams with varied achievement profiles (Mitigasi ~58% lowest → Statistik Distribusi ~98% highest) so team rankings, dashboard stats, and weekly recap pages can be verified. Safe to re-run: clears its own claims first (preserves real kip_activity_id-linked rows).
yDomain=[0,100] on a horizontal VisGroupedBar controls the CATEGORY axis (y in data-space = vertical bar positions), compressing 10 bars (indices 0-9) into ~9% of the 208px chart height — appearing as one single sliver. Fix: use xDomain=[0,100] which correctly caps the VALUE axis (bar lengths, horizontal) at 100%.
items_count was copying projects_count (project memberships), making both stat cards show the same number. Item Kerja now counts saved activity_claims for the current month — personal claims for members, team-wide claims for PJ. (Sukma: 18 proyek → 3 item kerja Juni 2026)
activity_claims and kip_activities are different tables — the weekly page (Kegiatan Saya) shows kip_activities, so Item Kerja must count the same source to stay consistent. Counts kip_activities.source_year for members; for PJ counts team members' kip_activities via project membership for the year.
Add subtitle "pribadi" for regular members and "seluruh tim" for PJ so the scope of the rata-rata capaian number is immediately obvious.
c6915ef to
3dfbcf9
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First slice of RFC-003 M1 — kipApp structure sync. Mirrors kipApp teams/projects/members/employees into local master tables. Verified live: 25 teams, 165 projects, 108 employees, 1169 project-member links. New: KipStructureSource + Api/Mock sources (monitoring/hirarki/daerah, proyek?timkerjaid, timkerja/anggota), SyncKipStructureAction (upsert by kip_external_id, provision employees by nip_lama, home team + leader, mirror project_members, additive employee_team), kinetik:sync-kip-structure command + admin button, token expiring-soon badge, NIP+team on employees list, kip_external_id migration. 18 new tests; full suite green (68); pint + vue-tsc clean; Playwright-verified. IKU→RK→IKI cascade split to a follow-up issue.