New landing page configuration - #4606
Conversation
Rewrite spec/plan/tasks for the configurable landing page feature to document all resolved architecture questions (Q-054-01..19) as full Decision Cards per spec-guidelines/open-questions-format.md, instead of loose prose. Q-054-01..10 cover the original design decisions (layout architecture, v8-only scope, SE gating, admin UI approach, dropped custom-CSS field, Mod Welcome category absorption, automatic+manual featured content, free-text link icons). Q-054-11..19 are gaps found during review and fixed directly: - landing_show_stats corrected to portfolio-only (was inconsistent) - confirmed the Featured tab's Search-based picker already surfaces private content to admin sessions (may_administrate bypass) - SE-gated dropdown options decided as badged-but-selectable, always showing the stored (not effective) config value - documented the intentional FR-054-26/S-054-27 numbering gap - clarified route registration needs both router/paths.ts and resources/js/v8/router/routes.ts - pinned the Mod Welcome filter citation to SettingsController.php:83 - designed the previously unspecified Reorder endpoint contract from scratch (no precedent existed anywhere in this codebase) - added missing icon length validation - confirmed studio's secondary CTA label is deliberately fixed open-questions.md rows updated to cross-reference the cards' option lettering; roadmap.md and _current-session.md updated accordingly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Included review availability: Your plan includes up to 8 reviews per rolling hour; 6 remain after this review. 📝 WalkthroughWalkthroughThis change adds a configurable v8 landing page with four layouts, configurable content, animations, links, featured items, administration, APIs, localization, self-hosted fonts, and regression tests. ChangesLanding page foundation
Supporting changes
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟡 Moderate · up to The landing-page configuration changes still contain several concrete behavior and consistency issues, including partial reorder updates, an ineffective social-media visibility toggle, unvalidated form submissions, and misleading configuration choices. The PR is not merge-ready until these issues are fixed or explicitly accepted by the owners. Poem
🚥 Pre-merge checks | ❌ 1❌ Failed checks (1 warning)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 🚀 New features to boost your workflow:
|
|
@coderabbitai please review! |
|
|
There was a problem hiding this comment.
Actionable comments posted: 6
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
docs/specs/4-architecture/features/054-configurable-landing-page/tasks.md (1)
373-378: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winFinish the Markdown task footer.
Line 346 already records
php artisan test --filter=LangTestas the confirmed translation check, but Line 375 says it still needs confirmation. Remove or update this stale TODO. The file also places_Last updated: 2026-08-11_at the top and ends without the required footer.As per coding guidelines: Markdown files must end with
---followed by*Last updated: [date of the update]*.Source: Coding guidelines
docs/specs/_current-session.md (1)
285-317: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winResolve the merge-conflict markers before merging.
Lines 285-317 contain
<<<<<<< HEAD,=======, and>>>>>>> master. The block combines incompatible Feature 054 and Feature 053 next steps. Line 12 states that Feature 053 is intentionally absent from this branch. Keep the Feature 054/052 state and remove the obsolete conflict branch.Source: Coding guidelines
🟡 Minor comments (21)
app/Http/Resources/Rights/ModulesRightsResource.php-172-182 (1)
172-182: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winDo not construct
Watermarkerfor a capability check.Line 182 resolves
Watermarker, which executes its constructor. The constructor callsis_watermark_enabled()and logs an error whenwatermark_photo_idis empty. An empty photo ID is the expected first-time configuration state for this availability flag.Expose the module check without constructing
Watermarker, for example as a static capability method, then call that method directly. This prevents false error logs on each rights-resource response before the administrator selects a watermark photo.Proposed fix
--- app/Image/Watermarker.php - public function is_module_enabled(): bool + public static function is_module_enabled(): bool - if (!$this->is_module_enabled()) { + if (!self::is_module_enabled()) { --- app/Http/Resources/Rights/ModulesRightsResource.php - return resolve(Watermarker::class)->is_module_enabled(); + return Watermarker::is_module_enabled();app/Http/Resources/GalleryConfigs/LandingPageResource.php-211-227 (1)
211-227: 🚀 Performance & Scalability | 🟡 Minor | ⚡ Quick winAvoid per-item queries for featured content.
Manual photo resolution loads
size_variantsonly, then the resource callsalbums()->first()for every photo. Album resolution already eager-loads cover relations, but the resource queries each cover again. A landing page with several featured items creates avoidable database queries.
app/Http/Resources/GalleryConfigs/LandingPageResource.php#L211-L227: Eager-load each manual photo's album ID withsize_variants.app/Http/Resources/GalleryConfigs/LandingFeaturedContentResource.php#L38-L59: Read the loadedalbums,cover, andmin_privilege_coverrelations instead of callingalbums()and queryingPhotoagain.resources/js/v8/components/forms/landing/LandingLinkFormDialog.vue-7-7 (1)
7-7: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winConnect the footer button to the form so native validation runs.
The
requiredandtype="url"constraints on Line 10 and Line 14 are only enforced when the form is submitted. The footer button sits outside the<form>and callssave()directly, so an empty label or a malformed URL is sent to the API. Give the form anidand make the button submit it.🔧 Proposed fix
- <form `@submit.prevent`="save"> + <form id="landing-link-form" `@submit.prevent`="save"><UButton + type="submit" + form="landing-link-form" :label="link ? $t('landing_link.save') : $t('landing_link.create')" color="primary" variant="solid" class="flex-1 justify-center font-bold" :loading="isSaving" - `@click`="save" />Also applies to: 42-49
resources/js/v8/views/admin/WatermarkPreview.vue-128-131 (1)
128-131: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winClickable
<label>reset controls are not keyboard accessible. All sites attach a reset click handler to a<label>and reveal the reset hint only on hover. The element is not focusable, so keyboard users cannot reach or trigger the reset, and the hint stays hidden for keyboard and touch users. Replace each<label>with<button type="button">and give it anaria-labelthat states the reset action.
resources/js/v8/views/admin/WatermarkPreview.vue#L128-L131: convert the horizontal shift label to a button; keepsliderX = 0as the click handler.resources/js/v8/views/admin/LandingConfig.vue#L165-L170: convert the CTA horizontal shift label to a button; keepsliderCtaX = 0as the click handler.resources/js/v8/views/admin/LandingConfig.vue#L197-L202: convert the CTA vertical shift label to a button; keepsliderCtaY = 0as the click handler.The vertical shift label in
resources/js/v8/views/admin/WatermarkPreview.vue#L177-L180needs the same change.resources/js/v8/views/admin/LandingConfig.vue-46-51 (1)
46-51: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPass
disabled-keyto the animationUSelectMenu.The layout menu at Line 26 sets
:disabled-key="'disabled'", so SE-locked layouts cannot be selected. The animation menu marks SE-locked entries withdisabledand renders an SE badge, but it does not setdisabled-key. A user can click a locked animation. The setter at Line 875 then discards the value without any feedback.🔧 Proposed fix
- <USelectMenu v-model="selectedAnimation" :items="animationOptions" label-key="label" class="w-1/2"> + <USelectMenu + v-model="selectedAnimation" + :items="animationOptions" + label-key="label" + :disabled-key="'disabled'" + class="w-1/2" + >resources/js/v8/components/settings/ConfigGroup.vue-222-228 (1)
222-228: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReturn
undefinedfor missing or non-numeric integer-range bounds.
NumberFielddeclaresmax?: number, sointRangeMaxcan returnundefined. Whenconfig.typeisint:1,intRangeMaxpassesNaNasmax.intRangeMinalso passesNaNfor a non-numeric minimum. Use a shared parser that returnsundefinedfor missing or non-finite segments, and keep the minimum fallback at0.app/Http/Requests/LandingLink/DestroyLandingLinkRequest.php-44-46 (1)
44-46: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse strict boolean checks for
is_built_in.
app/Http/Requests/LandingLink/DestroyLandingLinkRequest.php#L44-L46: replace the condition with$this->landing_link->is_built_in === true.app/Http/Requests/LandingLink/PatchLandingLinkRequest.php#L54-L56: replace the left condition with$this->landing_link->is_built_in === true.app/Http/Requests/LandingLink/UpdateLandingLinkRequest.php#L54-L56: replace the condition with$this->landing_link->is_built_in === true.As per coding guidelines, use strict comparison (
===) and only booleans inifstatements.Source: Coding guidelines
tests/Feature_v2/LandingLink/LandingLinkReorderTest.php-80-88 (1)
80-88: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winTest the unknown-ID validation path.
The request has two IDs while only one landing link exists. The endpoint can reject this request as an invalid complete list without checking
nonexistent-id.Create two landing links. Submit one valid ID and one unknown ID. Keep the submitted ID count equal to the persisted link count. Assert that both
sort_ordervalues remain unchanged.Proposed test adjustment
- $link1 = LandingLink::factory()->create(['sort_order' => 0]); + $link1 = LandingLink::factory()->create(['sort_order' => 1]); + $link2 = LandingLink::factory()->create(['sort_order' => 0]); $response = $this->actingAs($this->admin)->patchJson('LandingLink/Reorder', [ 'ids' => [$link1->id, 'nonexistent-id'], ]); $this->assertUnprocessable($response); + $this->assertSame(1, $link1->fresh()->sort_order); + $this->assertSame(0, $link2->fresh()->sort_order);DESIGN.md-18-28 (1)
18-28: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd the required update footer to both modified documentation files.
DESIGN.md#L18-L28: add an HR and*Last updated: August 16, 2026*at the end of the file.README.md#L78-L78: add an HR and*Last updated: August 16, 2026*at the end of the file.Source: Coding guidelines
app/Http/Resources/GalleryConfigs/FooterConfig.php-30-30 (1)
30-30: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winRename
sm_pixelfeed_urltosm_pixelfed_urlbefore release.The migration and resource keys match, but the public API and TypeScript contract misspell the platform name. Update all related references together.
resources/js/v8/components/landing/LandingMeridianRail.vue-91-102 (1)
91-102: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winKeep the no-intro reveal sequence synchronized with the rail duration. The rail duration is 0.6 seconds, but content begins after 0.13 or 0.28 seconds.
resources/js/v8/components/landing/LandingMeridianRail.vue#L91-L102: delay label and caption entrances until rail growth completes.resources/js/v8/views/landing/LandingMeridian.vue#L113-L114,L184-L186: delay background and header reveal until rail growth completes.resources/sass/app-v8.css-16-16 (1)
16-16: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse one unquoted
Vazirmatnfont-family identifier. Stylelint reports the samefont-family-name-quotesviolation in both locations.
resources/sass/app-v8.css#L16-L16: change"Vazirmatn"toVazirmatnin the global stack.resources/sass/fonts.css#L115-L176: change"Vazirmatn"toVazirmatnin every added@font-facedeclaration.Source: Linters/SAST tools
resources/js/v8/components/landing/LandingBackgroundField.vue-90-107 (1)
90-107: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPrevent stale photo resolutions.
Line 90 starts an untracked request. If requests overlap, an older response can assign its URL after a newer photo ID was entered or loaded. The preview can then show a different photo than
value.Track a request generation, or verify the response ID still matches
value.value.trim(), before assigningresolvedUrlorloadError.docs/specs/4-architecture/features/054-configurable-landing-page/plan.md-4-5 (1)
4-5: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winUse one verified feature completion state. The plan says Draft and has an unrun Analysis Gate, while the specification says Completed and mixes Active Features with Completed Features.
docs/specs/4-architecture/features/054-configurable-landing-page/plan.md#L4-L5: set the verified current state and record the completed Analysis Gate result.docs/specs/4-architecture/features/054-configurable-landing-page/spec.md#L5-L10: keep the status and roadmap entry consistent with the verified state, including the documentation deliverables.Source: Coding guidelines
docs/specs/4-architecture/features/054-configurable-landing-page/plan.md-228-235 (1)
228-235: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winAdd the required document footer.
Line 235 is the end of this Markdown document. Add an hr line followed by
*Last updated: 2026-08-11*after the backlog section.As per coding guidelines: “At the bottom of documentation files, add an hr line followed by "Last updated: [date of the update]".”
Source: Coding guidelines
docs/specs/4-architecture/roadmap.md-128-128 (1)
128-128: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winCorrect the roadmap footer metadata.
Line 20 records Feature 054 as completed on 2026-08-11 under Completed Features. Line 128 says it was added to Active Features on 2026-08-10. Update the footer to describe the move to Completed Features and use the current completion date.
Source: Coding guidelines
docs/specs/4-architecture/features/054-configurable-landing-page/tasks.md-6-8 (1)
6-8: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winOrder verification tasks before implementation tasks.
The checklist says “Stage tests before implementation,” but implementation tasks appear before their tests. For example, T-054-08 precedes T-054-12, and T-054-13 precedes T-054-19. Reorder each increment or split the entries so tests are listed first.
As per coding guidelines:
tasks.mdmust “mirror the plan, orders tests before code.” Based on learnings: keep increments bite-sized and update the checklist as each verification completes.Sources: Coding guidelines, Learnings
docs/specs/4-architecture/features/054-configurable-landing-page/tasks.md-25-25 (1)
25-25: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick winChange the stale task note to 3–100. The migration and FR-054-09 define
landing_featured_items_countasint:3:100; the Arabic and Japanese descriptions are already correct. Updatedocs/specs/4-architecture/features/054-configurable-landing-page/tasks.md#L25and leave both locale descriptions unchanged.lang/bg/all_settings.php-673-685 (1)
673-685: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse
meridianin both landing-setting descriptions.
lang/bg/all_settings.php: replaceminimalwithmeridian.lang/no/all_settings.php: apply the same correction.lang/el/all_settings.php-675-687 (1)
675-687: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winCorrect the landing-setting descriptions in both locale files.
- List
classic,portfolio,meridian, andstudioforlanding_layout; removeminimal.- State that
landing_intro_screen_enabledapplies to all four layouts.- Limit the hero text settings to
portfolio.- State that the about settings apply to
portfolioandstudio.- State that
landing_cta_textapplies toclassic,portfolio, andmeridian.lang/it/landing_config.php (1)
37-38: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winUse a descriptive label for the CTA text setting. Replace the incomplete
textlabel withCall-to-action textor the appropriate localized equivalent in each affected locale.
🧹 Nitpick comments (12)
resources/js/v8/views/admin/Settings.vue (1)
227-230: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider respecting
prefers-reduced-motion.Smooth scrolling can affect users who request reduced motion. Select the behavior from the media query.
♻️ Optional refactor
- window.scrollTo({ top: 0, behavior: "smooth" }); + const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches; + window.scrollTo({ top: 0, behavior: prefersReducedMotion ? "auto" : "smooth" });resources/js/v8/components/forms/settings/ColorField.vue (1)
147-160: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winNormalize the hex text on commit, not on every keystroke.
handleHexInputwrites the normalized value back into the bound input as soon as six digits are present. The displayed text changes under the caret, and the caret moves to the end. Editing a middle digit becomes difficult. Emit the value but leavehexInputas typed, and letrevertHexInputIfInvalidnormalize on blur.Also make the emitted case consistent.
handleChangeemitsnewColor.hexin its original case.handleHexInputemits an uppercase value. The stored configuration value then differs by input path.♻️ Proposed refactor
function handleChange(newColor: BlossomColorPickerColor) { val.value = newColor; hexInput.value = newColor.hex.toUpperCase(); - emits("filled", props.config.key, `${newColor.hex}`); + emits("filled", props.config.key, newColor.hex.toUpperCase()); } function handleHexInput(value: string | number) { const normalized = normalizeHexCandidate(String(value)); if (normalized === null) { // Not a complete hex code yet - leave it uncommitted until the user finishes typing. return; } - hexInput.value = normalized; val.value = hexToBlossomValue(normalized); emits("filled", props.config.key, normalized); } function revertHexInputIfInvalid() { - if (normalizeHexCandidate(hexInput.value) === null) { + const normalized = normalizeHexCandidate(hexInput.value); + if (normalized === null) { hexInput.value = resolveHex(props.config).toUpperCase(); + return; } + hexInput.value = normalized; }tests/Feature_v2/LandingPageResource/LandingFeaturedItemsAutomaticTest.php (1)
77-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the automatic-selection order.
This test checks only count and item type. An implementation that returns the oldest public albums would still pass.
Create albums with deterministic publication timestamps. Assert the expected IDs and order for the configured count.
package.json (1)
73-73: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winRemove the production
playwrightdependency.
playwrightis used only by development tooling. Remove the duplicate entry fromdependenciesatpackage.json:73.resources/js/v8/components/footers/LandingFooter.vue (1)
14-17: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the duplicated icon class list.
Both branches repeat the same
link-classarray. Move it to a single computed value and bind it in both places.♻️ Suggested consolidation
+const socialLinkClass = computed(() => [ + "inline-block hover:scale-150 transition-all ease-in-out duration-300 text-white text-2xl my-4 mx-5", + { "opacity-50 hover:opacity-100": props.dimIconsUntilHover === true }, +]);Then bind
:link-class="socialLinkClass"in both branches.Also applies to: 29-32
resources/js/v8/components/footers/SocialMediaLinks.vue (1)
2-93: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winReplace the twelve repeated anchors with a
v-for.Every block differs only by URL field, icon name, and label. A single array of
{ url, icon, label }plus onev-forremoves about 80 lines and prevents divergence when a new network is added.♻️ Suggested structure
+const networks = computed(() => + [ + { url: props.footerData.sm_facebook_url, icon: "social:facebook", label: "Facebook" }, + { url: props.footerData.sm_flickr_url, icon: "social:flickr", label: "Flickr" }, + // ...remaining networks + ].filter((n) => n.url !== ""), +);+ <a v-for="n in networks" :key="n.label" :href="n.url" :class="linkClass" target="_blank" rel="noopener" :aria-label="n.label"> + <UIcon :name="n.icon" /> + </a>resources/js/v8/components/landing/LandingPortfolioFeatured.vue (1)
84-86: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueUse the debounced layout in
onUpdated.
onUpdatedruns on every re-render of this component and starts a full masonry pass each time. The resize path already usesdebouncedLayout. Use the same function here to bound the work.onUpdated(() => { - runMasonryLayout(); + debouncedLayout(); });resources/js/v8/views/landing/LandingClassic.vue (2)
141-169: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the invalid
translatedeclaration and the unused.light-delayrule.Line 143 sets
translate: translateY(-300px);. The CSStranslateproperty accepts length or percentage components, not atranslateY()function. Browsers drop the declaration. The0%keyframe already setstransform: translateY(-300px), so the intent is covered.
.light-delayis declared in ascopedstyle block and is not applied by any element in this component. Scoped styles cannot reach other components, so the rule is dead.🧹 Proposed cleanup
.animate-landingAnimateDown { opacity: 0; - translate: translateY(-300px); animation-name: landingAnimateDown;- -.light-delay { - animation-delay: 0.5s !important; -}
124-138: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPlacement filtering and CTA fallback are duplicated across landing components. Each layout re-implements the same link placement filter and the same
cta_textfallback. A shared composable (for exampleuseLandingLinks) removes the duplication and keeps the placement rule in one place.
resources/js/v8/views/landing/LandingClassic.vue#L124-L138: replace the localnavLinksandctaTextcomputed values with the shared composable.resources/js/v8/views/landing/LandingPortfolio.vue#L110-L123: replace the identicalnavLinksandctaTextcomputed values with the same composable.resources/js/v8/components/footers/LandingFooter.vue#L91-L91: obtainfooterLinksfrom the shared placement filter instead of repeating the"footer" | "both"test.resources/js/v8/views/landing/LandingPortfolio.vue (1)
130-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMerge the two preset-to-class switches.
heroEntranceClassandsectionRevealClassshare four of five branches. Onlyparallax_scrolldiffers. One helper that maps the preset, with an optional override forparallax_scroll, removes the duplication.resources/js/v8/composables/landing/useLandingAnimation.ts (1)
15-26: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winUse
useMediaQueryfor reactive reduced-motion detection.
@vueuse/core14.4.0 exportsuseMediaQuery. Use it for"(prefers-reduced-motion: reduce)"soisReducedMotionupdates when the media query changes.resources/js/v8/views/landing/LandingStudio.vue (1)
110-114: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse promise chaining in this Vue component.
Replace
asyncandawaitwith.then(). The repository guideline requires this pattern in Vue3 files.As per coding guidelines: “Do not use await async calls in Vue3, use .then() instead.”
Source: Coding guidelines
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 9d8caf39-3cb4-44ff-8bcf-b9d4a0a9a4d5
⛔ Files ignored due to path filters (17)
package-lock.jsonis excluded by!**/package-lock.jsonresources/fonts/libre-franklin-v20-100.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-200.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-300.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-400.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-500.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-600.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-700.woff2is excluded by!**/*.woff2resources/fonts/libre-franklin-v20-900.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-100.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-200.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-300.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-400.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-500.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-600.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-700.woff2is excluded by!**/*.woff2resources/fonts/vazirmatn-900.woff2is excluded by!**/*.woff2
📒 Files selected for processing (273)
.gitignoreDESIGN.mdREADME.mdapp/Enum/LandingAnimationPreset.phpapp/Enum/LandingCtaPosition.phpapp/Enum/LandingFeaturedItemType.phpapp/Enum/LandingFeaturedItemsMode.phpapp/Enum/LandingLayoutType.phpapp/Enum/LandingLinkPlacement.phpapp/Enum/LandingLoginPosition.phpapp/Enum/LandingTextPosition.phpapp/Http/Controllers/Admin/LandingFeaturedItemController.phpapp/Http/Controllers/Admin/LandingLinkController.phpapp/Http/Controllers/Admin/SettingsController.phpapp/Http/Requests/LandingFeaturedItem/DestroyLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/IndexLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/PatchLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/ReorderLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/ShowLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/StoreLandingFeaturedItemRequest.phpapp/Http/Requests/LandingFeaturedItem/UpdateLandingFeaturedItemRequest.phpapp/Http/Requests/LandingLink/DestroyLandingLinkRequest.phpapp/Http/Requests/LandingLink/IndexLandingLinkRequest.phpapp/Http/Requests/LandingLink/PatchLandingLinkRequest.phpapp/Http/Requests/LandingLink/ReorderLandingLinkRequest.phpapp/Http/Requests/LandingLink/ShowLandingLinkRequest.phpapp/Http/Requests/LandingLink/StoreLandingLinkRequest.phpapp/Http/Requests/LandingLink/UpdateLandingLinkRequest.phpapp/Http/Resources/Collections/LandingFeaturedItemCollection.phpapp/Http/Resources/Collections/LandingLinkCollection.phpapp/Http/Resources/GalleryConfigs/FooterConfig.phpapp/Http/Resources/GalleryConfigs/LandingFeaturedContentResource.phpapp/Http/Resources/GalleryConfigs/LandingLinkEmbedResource.phpapp/Http/Resources/GalleryConfigs/LandingPageResource.phpapp/Http/Resources/Models/LandingFeaturedItemResource.phpapp/Http/Resources/Models/LandingLinkResource.phpapp/Http/Resources/Rights/ModulesRightsResource.phpapp/Image/Watermarker.phpapp/Models/Configs.phpapp/Models/LandingFeaturedItem.phpapp/Models/LandingLink.phpapp/Policies/AlbumQueryPolicy.phpapp/Rules/LandingFeaturedItemExistsRule.phpdatabase/factories/LandingFeaturedItemFactory.phpdatabase/factories/LandingLinkFactory.phpdatabase/migrations/2026_08_16_000000_add_contact_form_context_configs.phpdatabase/migrations/2026_08_16_000001_add_social_media_urls_config.phpdatabase/migrations/2026_08_16_000002_add_landing_page_configs.phpdatabase/migrations/2026_08_16_000003_create_landing_links_table.phpdatabase/migrations/2026_08_16_000004_create_landing_featured_items_table.phpdocs/specs/4-architecture/features/054-configurable-landing-page/plan.mddocs/specs/4-architecture/features/054-configurable-landing-page/spec.mddocs/specs/4-architecture/features/054-configurable-landing-page/tasks.mddocs/specs/4-architecture/open-questions.mddocs/specs/4-architecture/roadmap.mddocs/specs/_current-session.mdlang/ar/all_settings.phplang/ar/design-system.phplang/ar/landing.phplang/ar/landing_config.phplang/ar/landing_featured_item.phplang/ar/landing_link.phplang/ar/watermark.phplang/bg/all_settings.phplang/bg/design-system.phplang/bg/landing.phplang/bg/landing_config.phplang/bg/landing_featured_item.phplang/bg/landing_link.phplang/bg/watermark.phplang/cz/all_settings.phplang/cz/design-system.phplang/cz/landing.phplang/cz/landing_config.phplang/cz/landing_featured_item.phplang/cz/landing_link.phplang/cz/watermark.phplang/de/all_settings.phplang/de/design-system.phplang/de/landing.phplang/de/landing_config.phplang/de/landing_featured_item.phplang/de/landing_link.phplang/de/watermark.phplang/el/all_settings.phplang/el/design-system.phplang/el/landing.phplang/el/landing_config.phplang/el/landing_featured_item.phplang/el/landing_link.phplang/el/watermark.phplang/en/all_settings.phplang/en/design-system.phplang/en/landing.phplang/en/landing_config.phplang/en/landing_featured_item.phplang/en/landing_link.phplang/en/watermark.phplang/es/all_settings.phplang/es/design-system.phplang/es/landing.phplang/es/landing_config.phplang/es/landing_featured_item.phplang/es/landing_link.phplang/es/watermark.phplang/fa/all_settings.phplang/fa/design-system.phplang/fa/landing.phplang/fa/landing_config.phplang/fa/landing_featured_item.phplang/fa/landing_link.phplang/fa/watermark.phplang/fr/all_settings.phplang/fr/design-system.phplang/fr/landing.phplang/fr/landing_config.phplang/fr/landing_featured_item.phplang/fr/landing_link.phplang/fr/watermark.phplang/hu/all_settings.phplang/hu/design-system.phplang/hu/landing.phplang/hu/landing_config.phplang/hu/landing_featured_item.phplang/hu/landing_link.phplang/hu/watermark.phplang/it/all_settings.phplang/it/design-system.phplang/it/landing.phplang/it/landing_config.phplang/it/landing_featured_item.phplang/it/landing_link.phplang/it/watermark.phplang/ja/all_settings.phplang/ja/design-system.phplang/ja/landing.phplang/ja/landing_config.phplang/ja/landing_featured_item.phplang/ja/landing_link.phplang/ja/watermark.phplang/nl/all_settings.phplang/nl/design-system.phplang/nl/landing.phplang/nl/landing_config.phplang/nl/landing_featured_item.phplang/nl/landing_link.phplang/nl/watermark.phplang/no/all_settings.phplang/no/design-system.phplang/no/landing.phplang/no/landing_config.phplang/no/landing_featured_item.phplang/no/landing_link.phplang/no/watermark.phplang/pl/all_settings.phplang/pl/design-system.phplang/pl/landing.phplang/pl/landing_config.phplang/pl/landing_featured_item.phplang/pl/landing_link.phplang/pl/watermark.phplang/pt/all_settings.phplang/pt/design-system.phplang/pt/landing.phplang/pt/landing_config.phplang/pt/landing_featured_item.phplang/pt/landing_link.phplang/pt/watermark.phplang/ru/all_settings.phplang/ru/design-system.phplang/ru/landing.phplang/ru/landing_config.phplang/ru/landing_featured_item.phplang/ru/landing_link.phplang/ru/watermark.phplang/sk/all_settings.phplang/sk/design-system.phplang/sk/landing.phplang/sk/landing_config.phplang/sk/landing_featured_item.phplang/sk/landing_link.phplang/sk/watermark.phplang/sv/all_settings.phplang/sv/design-system.phplang/sv/landing.phplang/sv/landing_config.phplang/sv/landing_featured_item.phplang/sv/landing_link.phplang/sv/watermark.phplang/tr/all_settings.phplang/tr/design-system.phplang/tr/landing.phplang/tr/landing_config.phplang/tr/landing_featured_item.phplang/tr/landing_link.phplang/tr/watermark.phplang/vi/all_settings.phplang/vi/design-system.phplang/vi/landing.phplang/vi/landing_config.phplang/vi/landing_featured_item.phplang/vi/landing_link.phplang/vi/watermark.phplang/zh_CN/all_settings.phplang/zh_CN/design-system.phplang/zh_CN/landing.phplang/zh_CN/landing_config.phplang/zh_CN/landing_featured_item.phplang/zh_CN/landing_link.phplang/zh_CN/watermark.phplang/zh_TW/all_settings.phplang/zh_TW/design-system.phplang/zh_TW/landing.phplang/zh_TW/landing_config.phplang/zh_TW/landing_featured_item.phplang/zh_TW/landing_link.phplang/zh_TW/watermark.phppackage.jsonresources/fonts/OFL-libre-franklin.txtresources/fonts/OFL-vazirmatn.txtresources/js/lychee.d.tsresources/js/router/paths.tsresources/js/services/landing-featured-item-service.tsresources/js/services/landing-link-service.tsresources/js/v8/components/footers/GalleryFooter.vueresources/js/v8/components/footers/LandingFooter.vueresources/js/v8/components/footers/SocialMediaLinks.vueresources/js/v8/components/forms/auth/LoginForm.vueresources/js/v8/components/forms/landing/LandingLinkFormDialog.vueresources/js/v8/components/forms/settings/ColorField.vueresources/js/v8/components/landing/LandingBackdrop.vueresources/js/v8/components/landing/LandingBackgroundField.vueresources/js/v8/components/landing/LandingBackgroundImages.vueresources/js/v8/components/landing/LandingIntroScreen.vueresources/js/v8/components/landing/LandingMeridianRail.vueresources/js/v8/components/landing/LandingPortfolioFeatured.vueresources/js/v8/components/settings/ConfigGroup.vueresources/js/v8/components/settings/ConfirmSave.vueresources/js/v8/composables/landing/useLandingAnimation.tsresources/js/v8/composables/landing/useLandingBackgroundOrientation.tsresources/js/v8/composables/landing/useLandingCtaPosition.tsresources/js/v8/composables/landing/useLandingTextPosition.tsresources/js/v8/composables/useAdminTiles.tsresources/js/v8/composables/useScrollReveal.tsresources/js/v8/icons.tsresources/js/v8/router/routes.tsresources/js/v8/views/Error.vueresources/js/v8/views/Landing.vueresources/js/v8/views/admin/DesignSystem.vueresources/js/v8/views/admin/LandingConfig.vueresources/js/v8/views/admin/Settings.vueresources/js/v8/views/admin/WatermarkPreview.vueresources/js/v8/views/landing/LandingClassic.vueresources/js/v8/views/landing/LandingMeridian.vueresources/js/v8/views/landing/LandingPortfolio.vueresources/js/v8/views/landing/LandingStudio.vueresources/sass/app-v8.cssresources/sass/fonts.cssroutes/api_v2.phproutes/web_v2.phptests/Feature_v2/LandingFeaturedItem/LandingFeaturedItemCrudTest.phptests/Feature_v2/LandingFeaturedItem/LandingFeaturedItemReorderTest.phptests/Feature_v2/LandingLink/LandingLinkBuiltInProtectionTest.phptests/Feature_v2/LandingLink/LandingLinkCrudTest.phptests/Feature_v2/LandingLink/LandingLinkReorderTest.phptests/Feature_v2/LandingPageResource/LandingFeaturedItemsAutomaticTest.phptests/Feature_v2/LandingPageResource/LandingFeaturedItemsManualTest.phptests/Feature_v2/LandingPageResource/LandingPageContentTest.phptests/Feature_v2/LandingPageResource/LandingPageSeGatingTest.phptests/Traits/RequiresEmptyLandingFeaturedItems.phptests/Traits/RequiresEmptyLandingLinks.phptests/Unit/Actions/Db/OptimizeDbTest.phptests/Unit/Actions/Db/OptimizeTablesTest.php
💤 Files with no reviewable changes (1)
- resources/js/v8/views/admin/DesignSystem.vue
Included review availability: Your plan includes up to 8 reviews per rolling hour; 7 remain after this review.
Summary by CodeRabbit