part category grouping UI
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"/gunbuilder/page": "app/gunbuilder/page.js",
|
|
||||||
"/gunbuilder/admin/merchants/page": "app/gunbuilder/admin/merchants/page.js",
|
"/gunbuilder/admin/merchants/page": "app/gunbuilder/admin/merchants/page.js",
|
||||||
"/gunbuilder/[categoryId]/page": "app/gunbuilder/[categoryId]/page.js",
|
"/gunbuilder/page": "app/gunbuilder/page.js",
|
||||||
"/gunbuilder/build/page": "app/gunbuilder/build/page.js",
|
"/gunbuilder/build/page": "app/gunbuilder/build/page.js",
|
||||||
|
"/gunbuilder/[categoryId]/page": "app/gunbuilder/[categoryId]/page.js",
|
||||||
"/gunbuilder/admin/category-mappings/page": "app/gunbuilder/admin/category-mappings/page.js"
|
"/gunbuilder/admin/category-mappings/page": "app/gunbuilder/admin/category-mappings/page.js"
|
||||||
}
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -125,7 +125,7 @@
|
|||||||
/******/
|
/******/
|
||||||
/******/ /* webpack/runtime/getFullHash */
|
/******/ /* webpack/runtime/getFullHash */
|
||||||
/******/ (() => {
|
/******/ (() => {
|
||||||
/******/ __webpack_require__.h = () => ("c6d6d43de26b07e0")
|
/******/ __webpack_require__.h = () => ("f15ab610ff54783e")
|
||||||
/******/ })();
|
/******/ })();
|
||||||
/******/
|
/******/
|
||||||
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@ eval(__webpack_require__.ts("Promise.resolve(/*! import() eager */).then(__webpa
|
|||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"e2bb5f76af4b\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJlMmJiNWY3NmFmNGJcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"e222be0d16ad\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJlMjIyYmUwZDE2YWRcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||||
|
|
||||||
/***/ })
|
/***/ })
|
||||||
|
|
||||||
|
|||||||
@@ -192,7 +192,7 @@
|
|||||||
/******/
|
/******/
|
||||||
/******/ /* webpack/runtime/getFullHash */
|
/******/ /* webpack/runtime/getFullHash */
|
||||||
/******/ !function() {
|
/******/ !function() {
|
||||||
/******/ __webpack_require__.h = function() { return "d4224caeb46d3863"; }
|
/******/ __webpack_require__.h = function() { return "a6f46b17097acafc"; }
|
||||||
/******/ }();
|
/******/ }();
|
||||||
/******/
|
/******/
|
||||||
/******/ /* webpack/runtime/global */
|
/******/ /* webpack/runtime/global */
|
||||||
|
|||||||
@@ -672,6 +672,9 @@ video {
|
|||||||
.items-center {
|
.items-center {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
.items-baseline {
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
.justify-end {
|
.justify-end {
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
@@ -711,6 +714,16 @@ video {
|
|||||||
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
margin-top: calc(1rem * calc(1 - var(--tw-space-y-reverse)));
|
||||||
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
margin-bottom: calc(1rem * var(--tw-space-y-reverse));
|
||||||
}
|
}
|
||||||
|
.space-y-3 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-space-y-reverse: 0;
|
||||||
|
margin-top: calc(0.75rem * calc(1 - var(--tw-space-y-reverse)));
|
||||||
|
margin-bottom: calc(0.75rem * var(--tw-space-y-reverse));
|
||||||
|
}
|
||||||
|
.space-y-6 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-space-y-reverse: 0;
|
||||||
|
margin-top: calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));
|
||||||
|
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
||||||
|
}
|
||||||
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||||
--tw-divide-y-reverse: 0;
|
--tw-divide-y-reverse: 0;
|
||||||
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,22 @@
|
|||||||
|
"use strict";
|
||||||
|
/*
|
||||||
|
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||||
|
* This devtool is neither made for production nor for readable output files.
|
||||||
|
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||||
|
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||||
|
* or disable the default devtool with "devtool: false".
|
||||||
|
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||||
|
*/
|
||||||
|
self["webpackHotUpdate_N_E"]("app/layout",{
|
||||||
|
|
||||||
|
/***/ "(app-pages-browser)/./app/globals.css":
|
||||||
|
/*!*************************!*\
|
||||||
|
!*** ./app/globals.css ***!
|
||||||
|
\*************************/
|
||||||
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
|
eval(__webpack_require__.ts("__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__[\"default\"] = (\"e222be0d16ad\");\nif (true) { module.hot.accept() }\n//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiKGFwcC1wYWdlcy1icm93c2VyKS8uL2FwcC9nbG9iYWxzLmNzcyIsIm1hcHBpbmdzIjoiO0FBQUEsK0RBQWUsY0FBYztBQUM3QixJQUFJLElBQVUsSUFBSSxpQkFBaUIiLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9fTl9FLy4vYXBwL2dsb2JhbHMuY3NzPzk1YWEiXSwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGRlZmF1bHQgXCJlMjIyYmUwZDE2YWRcIlxuaWYgKG1vZHVsZS5ob3QpIHsgbW9kdWxlLmhvdC5hY2NlcHQoKSB9XG4iXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///(app-pages-browser)/./app/globals.css\n"));
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"c":["app/layout","app/gunbuilder/page","webpack"],"r":[],"m":[]}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
"use strict";
|
||||||
|
/*
|
||||||
|
* ATTENTION: An "eval-source-map" devtool has been used.
|
||||||
|
* This devtool is neither made for production nor for readable output files.
|
||||||
|
* It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
|
||||||
|
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
||||||
|
* or disable the default devtool with "devtool: false".
|
||||||
|
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
||||||
|
*/
|
||||||
|
self["webpackHotUpdate_N_E"]("webpack",{},
|
||||||
|
/******/ function(__webpack_require__) { // webpackRuntimeModules
|
||||||
|
/******/ /* webpack/runtime/getFullHash */
|
||||||
|
/******/ !function() {
|
||||||
|
/******/ __webpack_require__.h = function() { return "a6f46b17097acafc"; }
|
||||||
|
/******/ }();
|
||||||
|
/******/
|
||||||
|
/******/ }
|
||||||
|
);
|
||||||
File diff suppressed because one or more lines are too long
+131
-31
@@ -8,7 +8,7 @@ import type { CategoryId, Part } from "@/types/gunbuilder";
|
|||||||
import { CategoryColumn } from "@/components/CategoryColumn";
|
import { CategoryColumn } from "@/components/CategoryColumn";
|
||||||
|
|
||||||
type GunbuilderProductFromApi = {
|
type GunbuilderProductFromApi = {
|
||||||
id: number; // backend numeric id
|
id: number; // backend numeric id
|
||||||
name: string;
|
name: string;
|
||||||
brand: string;
|
brand: string;
|
||||||
platform: string;
|
platform: string;
|
||||||
@@ -39,6 +39,40 @@ type BuildState = Partial<Record<CategoryId, string>>; // categoryId -> partId
|
|||||||
|
|
||||||
const STORAGE_KEY = "gunbuilder-build-state";
|
const STORAGE_KEY = "gunbuilder-build-state";
|
||||||
|
|
||||||
|
// Group categories into logical sections.
|
||||||
|
//
|
||||||
|
// IMPORTANT:
|
||||||
|
// These IDs must match the ids you have in CATEGORIES.
|
||||||
|
// Anything that doesn't exist will just be skipped.
|
||||||
|
const CATEGORY_GROUPS: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
description?: string;
|
||||||
|
categoryIds: CategoryId[];
|
||||||
|
}[] = [
|
||||||
|
{
|
||||||
|
id: "lower-group",
|
||||||
|
label: "Lower Receiver Parts",
|
||||||
|
description:
|
||||||
|
"Everything from the serialized lower to small parts and core controls.",
|
||||||
|
categoryIds: ["lower", "lowerParts", "stock", "buffer"] as CategoryId[],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "upper-group",
|
||||||
|
label: "Upper Receiver Parts",
|
||||||
|
description:
|
||||||
|
"Barrel, upper, handguard, and the parts that keep the rifle cycling.",
|
||||||
|
categoryIds: [
|
||||||
|
"upper",
|
||||||
|
"barrel",
|
||||||
|
"handguard",
|
||||||
|
"chargingHandle",
|
||||||
|
"sights",
|
||||||
|
"optic",
|
||||||
|
] as CategoryId[],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export default function GunbuilderPage() {
|
export default function GunbuilderPage() {
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -87,7 +121,7 @@ export default function GunbuilderPage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(p.id), // <<< ALWAYS string
|
id: String(p.id), // ALWAYS string
|
||||||
categoryId,
|
categoryId,
|
||||||
name: p.name,
|
name: p.name,
|
||||||
brand: p.brand,
|
brand: p.brand,
|
||||||
@@ -124,7 +158,9 @@ export default function GunbuilderPage() {
|
|||||||
...prev,
|
...prev,
|
||||||
[categoryId as CategoryId]: partId,
|
[categoryId as CategoryId]: partId,
|
||||||
};
|
};
|
||||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
if (typeof window !== "undefined") {
|
||||||
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
|
||||||
|
}
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -170,6 +206,15 @@ export default function GunbuilderPage() {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Use group ordering for the summary as well
|
||||||
|
const summaryCategoryOrder: CategoryId[] = useMemo(
|
||||||
|
() =>
|
||||||
|
CATEGORY_GROUPS.flatMap((group) => group.categoryIds).filter(
|
||||||
|
(id) => CATEGORIES.some((c) => c.id === id),
|
||||||
|
),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen bg-black text-zinc-50">
|
<main className="min-h-screen bg-black text-zinc-50">
|
||||||
<div className="border-b border-amber-500/20 bg-amber-500/5">
|
<div className="border-b border-amber-500/20 bg-amber-500/5">
|
||||||
@@ -179,7 +224,8 @@ export default function GunbuilderPage() {
|
|||||||
Early Access Beta
|
Early Access Beta
|
||||||
</span>
|
</span>
|
||||||
<span className="hidden text-amber-100/90 md:inline">
|
<span className="hidden text-amber-100/90 md:inline">
|
||||||
You're using an early-access prototype of The Armory. Data, pricing, and available parts are still evolving.
|
You're using an early-access prototype of The Armory. Data,
|
||||||
|
pricing, and available parts are still evolving.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-amber-100/90 md:hidden">
|
<span className="text-amber-100/90 md:hidden">
|
||||||
@@ -198,7 +244,10 @@ export default function GunbuilderPage() {
|
|||||||
The Armory: <span className="text-amber-300">Early Access</span>
|
The Armory: <span className="text-amber-300">Early Access</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="mt-2 text-sm text-zinc-400 max-w-xl">
|
<p className="mt-2 text-sm text-zinc-400 max-w-xl">
|
||||||
Explore components from trusted brands, choose one part per category, track live prices, and watch your total build cost update as you go. This early-access builder keeps your setup saved locally so you can come back and refine it anytime.
|
Explore components from trusted brands, choose one part per
|
||||||
|
category, track live prices, and watch your total build cost
|
||||||
|
update as you go. This early-access builder keeps your setup saved
|
||||||
|
locally so you can come back and refine it anytime.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -218,7 +267,9 @@ export default function GunbuilderPage() {
|
|||||||
{/* Categories/parts */}
|
{/* Categories/parts */}
|
||||||
<section className="rounded-lg border border-zinc-800 bg-zinc-950/60 p-3 md:p-4">
|
<section className="rounded-lg border border-zinc-800 bg-zinc-950/60 p-3 md:p-4">
|
||||||
{loading && (
|
{loading && (
|
||||||
<p className="text-sm text-zinc-500">Loading parts from backend…</p>
|
<p className="text-sm text-zinc-500">
|
||||||
|
Loading parts from backend…
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
{error && !loading && (
|
{error && !loading && (
|
||||||
<p className="text-sm text-red-400">
|
<p className="text-sm text-red-400">
|
||||||
@@ -227,22 +278,51 @@ export default function GunbuilderPage() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{!loading && !error && (
|
{!loading && !error && (
|
||||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
<div className="space-y-6">
|
||||||
{CATEGORIES.map((category) => (
|
{CATEGORY_GROUPS.map((group) => {
|
||||||
<div
|
const groupCategories = CATEGORIES.filter((c) =>
|
||||||
key={category.id}
|
group.categoryIds.includes(c.id as CategoryId),
|
||||||
className="border border-zinc-800 rounded-md p-2 md:p-3 bg-zinc-950/60"
|
);
|
||||||
>
|
|
||||||
<CategoryColumn
|
if (groupCategories.length === 0) {
|
||||||
category={category}
|
return null;
|
||||||
parts={partsByCategory[category.id]}
|
}
|
||||||
selectedPartId={build[category.id]}
|
|
||||||
onSelectPart={(partId) =>
|
return (
|
||||||
handleSelectPart(category.id, partId)
|
<div key={group.id} className="space-y-3">
|
||||||
}
|
<div className="flex items-baseline justify-between gap-2">
|
||||||
/>
|
<div>
|
||||||
</div>
|
<h2 className="text-sm font-semibold text-zinc-100">
|
||||||
))}
|
{group.label}
|
||||||
|
</h2>
|
||||||
|
{group.description && (
|
||||||
|
<p className="text-xs text-zinc-500 mt-1 max-w-xl">
|
||||||
|
{group.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||||
|
{groupCategories.map((category) => (
|
||||||
|
<div
|
||||||
|
key={category.id}
|
||||||
|
className="border border-zinc-800 rounded-md p-2 md:p-3 bg-zinc-950/60"
|
||||||
|
>
|
||||||
|
<CategoryColumn
|
||||||
|
category={category}
|
||||||
|
parts={partsByCategory[category.id]}
|
||||||
|
selectedPartId={build[category.id]}
|
||||||
|
onSelectPart={(partId) =>
|
||||||
|
handleSelectPart(category.id, partId)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
@@ -261,11 +341,16 @@ export default function GunbuilderPage() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<ul className="space-y-2">
|
<ul className="space-y-2">
|
||||||
{CATEGORIES.map((cat) => {
|
{summaryCategoryOrder.map((categoryId) => {
|
||||||
|
const cat = CATEGORIES.find((c) => c.id === categoryId);
|
||||||
|
if (!cat) return null;
|
||||||
|
|
||||||
const selectedPartId = build[cat.id];
|
const selectedPartId = build[cat.id];
|
||||||
const part = parts.find(
|
const part = parts.find(
|
||||||
(p) => p.id === selectedPartId && p.categoryId === cat.id,
|
(p) =>
|
||||||
|
p.id === selectedPartId && p.categoryId === cat.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={cat.id}>
|
<li key={cat.id}>
|
||||||
<div className="text-[0.7rem] uppercase tracking-[0.16em] text-zinc-500">
|
<div className="text-[0.7rem] uppercase tracking-[0.16em] text-zinc-500">
|
||||||
@@ -310,7 +395,9 @@ export default function GunbuilderPage() {
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setBuild({});
|
setBuild({});
|
||||||
localStorage.removeItem(STORAGE_KEY);
|
if (typeof window !== "undefined") {
|
||||||
|
localStorage.removeItem(STORAGE_KEY);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
disabled={selectedParts.length === 0}
|
disabled={selectedParts.length === 0}
|
||||||
className={`w-full rounded-md border border-zinc-700 bg-zinc-900/50 px-3 py-2 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:border-zinc-600 transition-colors ${
|
className={`w-full rounded-md border border-zinc-700 bg-zinc-900/50 px-3 py-2 text-sm font-medium text-zinc-300 hover:bg-zinc-800 hover:border-zinc-600 transition-colors ${
|
||||||
@@ -327,27 +414,40 @@ export default function GunbuilderPage() {
|
|||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* What's New */}
|
||||||
<section className="mt-8 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4">
|
<section className="mt-8 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4">
|
||||||
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-300">
|
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-300">
|
||||||
What's New in Early Access
|
What's New in Early Access
|
||||||
</h2>
|
</h2>
|
||||||
<ul className="mt-2 space-y-1 text-sm text-zinc-400">
|
<ul className="mt-2 space-y-1 text-sm text-zinc-400">
|
||||||
<li>• Live parts and pricing pulled from multiple merchants.</li>
|
<li>• Live parts and pricing pulled from multiple merchants.</li>
|
||||||
<li>• Category-based layout so you can see at a glance which parts of your build are still missing.</li>
|
<li>
|
||||||
<li>• Running build total that updates automatically as you add or swap components.</li>
|
• Grouped layout for lower and upper receiver parts so you can see
|
||||||
<li>• Local build persistence so your selections stick around between visits on this device.</li>
|
at a glance which sections of your build are still missing.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
• Running build total that updates automatically as you add or
|
||||||
|
swap components.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
• Local build persistence so your selections stick around between
|
||||||
|
visits on this device.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
{/* Roadmap */}
|
||||||
<section className="mt-8 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4">
|
<section className="mt-8 rounded-lg border border-zinc-800 bg-zinc-950/80 p-3 md:p-4">
|
||||||
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-300">
|
<h2 className="text-xs font-semibold uppercase tracking-[0.16em] text-amber-300">
|
||||||
Whats on our roadmap
|
What's on our roadmap
|
||||||
</h2>
|
</h2>
|
||||||
<ul className="mt-2 space-y-1 text-sm text-zinc-400">
|
<ul className="mt-2 space-y-1 text-sm text-zinc-400">
|
||||||
<li>• Platform filters (AR-15, AR-9, AR-10)</li>
|
<li>• Platform filters (AR-15, AR-9, AR-10)</li>
|
||||||
<li>• More part categories</li>
|
<li>• More part categories and furniture</li>
|
||||||
<li>• Richer pricing/stock sync</li>
|
<li>• Richer pricing/stock sync</li>
|
||||||
<li>• Smarter compatibility checks between components</li>
|
<li>• Smarter compatibility checks between components</li>
|
||||||
<li>• AR9/AR10 support And more</li>
|
<li>• AR9/AR10 support and more</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user