From b66ee00361e07d06b0f99f79a722f11a1c9fb025 Mon Sep 17 00:00:00 2001 From: Robbie Paul Date: Wed, 29 Apr 2026 00:57:55 +0100 Subject: [PATCH] Update --- app/controllers/admins_controller.rb | 2 +- app/controllers/listings_controller.rb | 52 +++++++------- .../controllers/listing_puzzle_controller.js | 9 ++- app/views/admins/show.html.erb | 25 ++++--- app/views/listings/show.html.erb | 69 ++++++++++--------- config/routes.rb | 2 +- docs/forecourt-puzzle-clues.md | 63 ++++++++--------- test/integration/motorlot_flow_test.rb | 6 +- 8 files changed, 119 insertions(+), 109 deletions(-) diff --git a/app/controllers/admins_controller.rb b/app/controllers/admins_controller.rb index c8ecd9b..d935cca 100644 --- a/app/controllers/admins_controller.rb +++ b/app/controllers/admins_controller.rb @@ -12,7 +12,7 @@ class AdminsController < ApplicationController else session[:admin_unlocked] = false @unlocked = false - flash.now[:alert] = "That passphrase did not unlock anything. Check the advert again." + flash.now[:alert] = "That passphrase did not unlock anything. Check the stock page again." render :show, status: :unprocessable_entity end end diff --git a/app/controllers/listings_controller.rb b/app/controllers/listings_controller.rb index c27d1c4..d695285 100644 --- a/app/controllers/listings_controller.rb +++ b/app/controllers/listings_controller.rb @@ -13,10 +13,12 @@ class ListingsController < ApplicationController stock_number: "FC-718-421", exterior: "Graphite Blue Metallic", interior: "Black leather with chalk stitching", - seller_name: "Amelia Bennett", - seller_role: "Verified private seller", - seller_since: "On Forecourt since 2019", - response_time: "Usually replies within 14 minutes", + dealer_name: "Forecourt Specialist Cars", + dealer_tagline: "Indoor showroom ยท Appointment-led viewings", + dealer_since: "Established 2012", + dealer_contact: "01483 905210", + dealer_hours: "Mon-Sat 09:00-17:30", + dealer_address: "Woodbridge Meadows, Guildford", image_caption: "Photo set 03 // ingest note JHDU // something seems shifted.", hero_image: "https://images.unsplash.com/photo-1494976388531-d1058494cdd8?auto=format&fit=crop&w=1600&q=80", gallery: [ @@ -27,11 +29,11 @@ class ListingsController < ApplicationController highlights: [ "Original paint-depth readings documented in the gallery", "Fresh Michelin PS4S tyres with under 1,500 miles", - "Annual brake fluid and gearbox service invoices retained from the seller's specialist" + "Annual brake fluid and gearbox service invoices retained in the vehicle file" ], overview: [ - "This Cayman S was specced exactly the way enthusiasts usually hope to find one: manual gearbox, Sport Chrono, PASM, and no unnecessary aero add-ons. The seller has owned it for four years, used it as a weekend car, and kept every service invoice in order.", - "Cosmetically it presents like a well-kept private sale rather than a detail-heavy showroom car. There are two tiny stone marks on the nose, the front splitter shows light road wear, and the cabin leather has stayed impressively matte.", + "This Cayman S was specced exactly the way enthusiasts usually hope to find one: manual gearbox, Sport Chrono, PASM, and no unnecessary aero add-ons. Forecourt acquired it from a long-term local owner and has kept the supporting service file in order.", + "Cosmetically it presents like an honestly prepared specialist-stock car rather than an over-restored showroom piece. There are two tiny stone marks on the nose, the front splitter shows light road wear, and the cabin leather has stayed impressively matte.", "Forecourt's intake notes show an HPI-clear history, no insurance loss markers, two keys, the original order paperwork, and recent borescope images from the last annual inspection." ], specs: [ @@ -45,13 +47,13 @@ class ListingsController < ApplicationController [ "Keepers", "2 keepers" ], [ "Vehicle history", "HPI clear, no outstanding finance" ] ], - document_packet: [ - { source: "Original order form", detail: "Factory spec confirmed against supplied paperwork", file_type: "PDF" }, - { source: "Annual alignment sheet", detail: "Alignment printout tucked behind service invoices", file_type: "Scan" }, - { source: "Battery conditioner leaflet", detail: "Charger notes only, no service relevance", file_type: "JPG" }, - { source: "Paint protection card", detail: "XPEL warranty card added to the folder", file_type: "Scan" }, - { source: "Key handover note", detail: "Leather sleeve re-dyed to match the cabin", file_type: "Scan" }, - { source: "Roadside kit insert", detail: "Emergency cartridge dated and photographed", file_type: "Scan" } + factory_options: [ + { code: "AAG", option: "Aluminium look fuel cap", verified_from: "Order form" }, + { code: "P04", option: "Automatically dimming mirrors", verified_from: "Photo match" }, + { code: "XLS", option: "Xenon headlamps with PDLS", verified_from: "Order form" }, + { code: "QR5", option: "Sport Chrono display surround", verified_from: "Prep sheet" }, + { code: "LTH", option: "Leather steering column casing", verified_from: "Order form" }, + { code: "EPK", option: "Extended interior package", verified_from: "Order form" } ], condition_notes: [ "Cold-start video archived from 24 April", @@ -59,18 +61,18 @@ class ListingsController < ApplicationController "Underbody photos show no corrosion bloom", "Driver's bolster has only light creasing" ], - seller_notes: [ - "Always warmed through before spirited driving", - "No track days, no winter salt exposure", - "Super unleaded only, documented with fuel log", - "Includes factory battery conditioner and indoor cover" + dealer_notes: [ + "Two keys, tracker cards and handbook pack present", + "Fresh MOT issued ahead of photography", + "Geometry check filed with the vehicle history pack", + "Viewings by appointment in the indoor showroom" ], - prep_tickets: [ - { task: "Bay card print", stamp: "2026-04-24 08:31 BST", code: "PDI-24-09", status: "Open" }, - { task: "Handover pack filed", stamp: "2026-04-24 08:27 BST", code: "PDI-24-11", status: "Closed" }, - { task: "Road test sign-off", stamp: "2026-04-24 08:21 BST", code: "PDI-24-18", status: "Closed" }, - { task: "Alarm fob check", stamp: "2026-04-24 08:16 BST", code: "PDI-24-01", status: "Closed" }, - { task: "Paint-depth sheet scanned", stamp: "2026-04-24 08:12 BST", code: "PDI-24-16", status: "Closed" } + inspection_log: [ + { note: "Front splitter edge photographed", stamp: "2026-04-24 08:31 BST", code: "OBS-17-09", status: "Advisory" }, + { note: "Battery conditioner case checked", stamp: "2026-04-24 08:27 BST", code: "INS-88-52", status: "Filed" }, + { note: "Road test complete", stamp: "2026-04-24 08:21 BST", code: "INS-88-73", status: "Filed" }, + { note: "Alarm fob battery confirmed", stamp: "2026-04-24 08:16 BST", code: "INS-88-21", status: "Filed" }, + { note: "Paint-depth sheet attached", stamp: "2026-04-24 08:12 BST", code: "INS-88-71", status: "Filed" } ] } diff --git a/app/javascript/controllers/listing_puzzle_controller.js b/app/javascript/controllers/listing_puzzle_controller.js index 2679d39..79aa198 100644 --- a/app/javascript/controllers/listing_puzzle_controller.js +++ b/app/javascript/controllers/listing_puzzle_controller.js @@ -1,13 +1,16 @@ import { Controller } from "@hotwired/stimulus" +const REVIEW_PATH = "/internal/pdi-bundle-7c4f" + export default class extends Controller { - static targets = ["hintPanel", "progress"] + static targets = ["hintPanel", "progress", "reviewPath"] connect() { this.clickCount = 0 - console.info("[Forecourt QA] Local review tools remain mounted at /admin.") - console.debug("[Forecourt QA] One payload on this page is encoded for transport, not encrypted.") + if (this.hasReviewPathTarget) { + this.reviewPathTarget.textContent = REVIEW_PATH + } } tapLogo() { diff --git a/app/views/admins/show.html.erb b/app/views/admins/show.html.erb index cd3190a..c770370 100644 --- a/app/views/admins/show.html.erb +++ b/app/views/admins/show.html.erb @@ -7,7 +7,7 @@

Forecourt

Local admin console

- <%= link_to "Back to advert", root_path, class: "inline-flex items-center rounded-full border border-white/15 px-4 py-2 text-sm font-medium text-zinc-200 transition hover:border-white/30 hover:text-white" %> + <%= link_to "Back to vehicle", root_path, class: "inline-flex items-center rounded-full border border-white/15 px-4 py-2 text-sm font-medium text-zinc-200 transition hover:border-white/30 hover:text-white" %> @@ -35,19 +35,19 @@
@@ -62,15 +62,15 @@
-

Manual seller review

-

Awaiting notes from trust-and-safety.

+

Manual stock review

+

Awaiting sign-off from the prep team.

Homepage merchandising slot 02

@@ -114,9 +114,12 @@

Restricted access

Admin login required

- Enter the four-word access phrase from the advert page in the format + Enter the four-word access phrase from the stock page in the format WORD_1-WORD_2-WORD_3-WORD_4.

+

+ Use the words in page order. +

<%= form_with url: admin_path, method: :post, class: "mt-8 space-y-5" do |form| %>
diff --git a/app/views/listings/show.html.erb b/app/views/listings/show.html.erb index 85a1d17..a29682b 100644 --- a/app/views/listings/show.html.erb +++ b/app/views/listings/show.html.erb @@ -1,8 +1,6 @@ <% content_for :title, "#{@listing[:title]} | Forecourt" %>
- - @@ -38,7 +37,7 @@
-

Advert FC-718-421

+

Stock FC-718-421

<%= @listing[:title] %>

<%= @listing[:subtitle] %>

@@ -92,8 +91,8 @@
- - + +
@@ -116,12 +115,14 @@
-

Seller

-

<%= @listing[:seller_name] %>

-

<%= @listing[:seller_role] %>

+

Dealership

+

<%= @listing[:dealer_name] %>

+

<%= @listing[:dealer_tagline] %>

-

<%= @listing[:seller_since] %>

-

<%= @listing[:response_time] %>

+

<%= @listing[:dealer_since] %>

+

<%= @listing[:dealer_hours] %>

+

<%= @listing[:dealer_address] %>

+

<%= @listing[:dealer_contact] %>

@@ -163,20 +164,20 @@
-

Document packet

-

The seller uploaded the original paperwork bundle and a few newer scans. Check the details carefully.

+

Factory options

+

The stock sheet is longer than the original order form. Check the verified items carefully.

-
-

Source

-

Detail

-

File

+
+

Code

+

Option

+

Verified

- <% @listing[:document_packet].each do |entry| %> -
-

<%= entry[:source] %>

-

<%= entry[:detail] %>

+ <% @listing[:factory_options].each do |entry| %> +
+

<%= entry[:code] %>

+

<%= entry[:option] %>

- <%= entry[:file_type] %> + <%= entry[:verified_from] %>
<% end %> @@ -186,33 +187,33 @@
-

Inspection notes

+

Condition summary

<% @listing[:condition_notes].each do |note| %>
<%= note %>
<% end %>
-

Seller notes

+

Dealer notes

- <% @listing[:seller_notes].each do |note| %> + <% @listing[:dealer_notes].each do |note| %>

<%= note %>

<% end %>
-

Pre-sale prep tickets

-

The prep desk export is still sorted by latest activity. Closed tickets all share the same base code, and only one part changes.

+

Inspection notes

+

The workshop printout is still shown newest first. Only the notes filed into the final inspection bundle share the same base code, and the service desk phone still uses the old multi-tap keypad labels.

- <% @listing[:prep_tickets].each do |entry| %> + <% @listing[:inspection_log].each do |entry| %>
-

<%= entry[:task] %>

+

<%= entry[:note] %>

<%= entry[:stamp] %>

- <%= entry[:status] %> + <%= entry[:status] %> <%= entry[:code] %>
@@ -224,9 +225,9 @@
-

Forecourt promise

+

Forecourt promise

- Every enthusiast advert includes verified vehicle-history data, recent visual documentation, and just enough intake weirdness to remind you a human touched it somewhere along the way. + Every car in stock includes verified vehicle-history data, recent visual documentation, and just enough intake weirdness to remind you a human touched it somewhere along the way.

diff --git a/config/routes.rb b/config/routes.rb index f4277f6..8b3fd34 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,5 +11,5 @@ Rails.application.routes.draw do root "listings#show" get "listings/graphite-cayman-s", to: "listings#show", as: :listing - resource :admin, only: %i[show create destroy], controller: "admins" + resource :admin, path: "internal/pdi-bundle-7c4f", only: %i[show create destroy], controller: "admins" end diff --git a/docs/forecourt-puzzle-clues.md b/docs/forecourt-puzzle-clues.md index d413863..1bdad44 100644 --- a/docs/forecourt-puzzle-clues.md +++ b/docs/forecourt-puzzle-clues.md @@ -8,35 +8,34 @@ This file is for the puzzle owner only and should not be linked from the UI. ## Route -- Hidden route: `/admin` -- Discovery points on the advert page: - - HTML comment near the top of the page source - - Browser console message from the Stimulus controller - - Optional hidden hint drawer after clicking the Forecourt logo 5 times +- Hidden route: `/internal/pdi-bundle-7c4f` +- Discovery points on the stock page: + - Stimulus controller source for the stock page (`listing_puzzle_controller`) + - Optional hidden hint drawer after clicking the Forecourt logo 5 times (the drawer renders the controller's stored path at runtime) ## Stage 1 -- Location: main image caption on the advert page +- Location: main image caption on the stock page - Visible clue: `Photo set 03 // ingest note JHDU // something seems shifted.` - Solve method: Caesar shift back by 3 - Answer: `GEAR` ## Stage 2 -- Location: "Document packet" table -- Visible hint: `The seller uploaded the original paperwork bundle and a few newer scans. Check the details carefully.` +- Location: "Factory options" table +- Visible hint: `The stock sheet is longer than the original order form. Check the verified items carefully.` - Solve method: - - use only the rows marked `Scan` - - then take the first letter of each entry in the `Detail` column - - Alignment printout tucked behind service invoices - - XPEL warranty card added to the folder - - Leather sleeve re-dyed to match the cabin - - Emergency cartridge dated and photographed + - use only the rows verified from `Order form` + - then take the first letter of each option name in top-to-bottom order + - Aluminium look fuel cap + - Xenon headlamps with PDLS + - Leather steering column casing + - Extended interior package - Answer: `AXLE` ## Stage 3 -- Location: hidden JSON blob in the advert page source +- Location: hidden JSON blob in the stock page source - Element ID: `forecourt-media-manifest` - Encoded value: `VFVSQk8=` - Solve method: Base64 decode @@ -44,26 +43,28 @@ This file is for the puzzle owner only and should not be linked from the UI. ## Stage 4 -- Location: "Pre-sale prep tickets" card -- Visible hint: `The prep desk export is still sorted by latest activity. Closed tickets all share the same base code, and only one part changes.` -- Ticket codes: - - `PDI-24-09` (`Open`, ignore this row) - - `PDI-24-11` - - `PDI-24-18` - - `PDI-24-01` - - `PDI-24-16` +- Location: "Inspection notes" card +- Visible hint: `The workshop printout is still shown newest first. Only the notes filed into the final inspection bundle share the same base code, and the service desk phone still uses the old multi-tap keypad labels.` +- Note references: + - `OBS-17-09` (`Advisory`, ignore this row) + - `INS-88-52` + - `INS-88-73` + - `INS-88-21` + - `INS-88-71` - Solve method: - - use only the `Closed` tickets - - the card is shown newest first, so read the closed tickets from oldest to newest - - map the changing numeric suffixes with A=1 - - 16 = P - - 01 = A - - 18 = R - - 11 = K + - use only the notes marked `Filed` + - ignore the one advisory entry with a different base code + - the card is shown newest first, so read the filed notes from oldest to newest + - decode the final two digits as classic mobile keypad `key + tap count` + - 71 = P (`7` pressed once) + - 21 = A (`2` pressed once) + - 73 = R (`7` pressed three times) + - 52 = K (`5` pressed twice) - Answer: `PARK` ## Admin behavior -- `GET /admin` shows the login prompt +- `GET /internal/pdi-bundle-7c4f` shows the login prompt +- The login prompt tells solvers to use the words in page order - Entering `GEAR-AXLE-TURBO-PARK` unlocks the fake admin panel - The panel is session-backed only and can be cleared with the "Clear admin session" button diff --git a/test/integration/motorlot_flow_test.rb b/test/integration/motorlot_flow_test.rb index 86539a3..e969683 100644 --- a/test/integration/motorlot_flow_test.rb +++ b/test/integration/motorlot_flow_test.rb @@ -1,14 +1,14 @@ require "test_helper" class MotorlotFlowTest < ActionDispatch::IntegrationTest - test "advert page renders puzzle listing" do + test "stock page renders puzzle listing" do get root_path assert_response :success assert_includes response.body, "2021 Porsche 718 Cayman S" assert_includes response.body, "JHDU" assert_includes response.body, "VFVSQk8=" - assert_includes response.body, "PDI-24-16" + assert_includes response.body, "INS-88-71" end test "admin unlock succeeds with the full password" do @@ -19,7 +19,7 @@ class MotorlotFlowTest < ActionDispatch::IntegrationTest assert_response :success assert_includes response.body, "Admin Panel Unlocked" - assert_includes response.body, "Delete adverts" + assert_includes response.body, "Delete stock page" end test "admin unlock rejects invalid passwords" do