Add HL3D branding, Etsy Live/Draft paging and Projects lifecycle
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 23s

Replace the old touchmark/PNG identity with the supplied lockup and
monogram, segmented role-aware navigation, Live/Draft listing filters
with generation-based cache invalidation, and admin Projects covering
immutable file revisions, listing live pointers, and exact-decimal
filament/packaging/shipping costing. Additive schema only; no Etsy
publication. Isolated clone tests: 301 tests, 1162 assertions.
This commit is contained in:
2026-11-08 12:50:56 +00:00
parent c8c5822166
commit d4928789c4
83 changed files with 5808 additions and 84 deletions
+52 -4
View File
@@ -11,25 +11,73 @@ use Throwable;
class EtsyController extends Controller
{
/** @var list<string> */
private const UI_STATES = ['active', 'draft'];
private const PER_PAGE = 20;
public function index(Request $request, Etsy $etsy): View
{
abort_unless($request->user()->role === 'admin', 403);
$connection = EtsyConnection::first();
$receipts = [];
$listings = [];
$integrationError = null;
$listingCount = 0;
$receiptsError = null;
$listingsError = null;
$stateError = null;
$pageError = null;
$rawState = $request->query('state', 'active');
$state = is_string($rawState) ? strtolower($rawState) : '';
if (! in_array($state, self::UI_STATES, true)) {
$stateError = 'This is not a supported listing state. Select Live or Drafts.';
$state = 'active';
}
$rawPage = $request->query('page', '1');
if (! is_string($rawPage) || ! ctype_digit($rawPage) || strlen($rawPage) > 5 || (int) $rawPage < 1 || (int) $rawPage > 10000) {
$pageError = 'This is not a valid page number. Use pages 1–10000.';
$page = 1;
} else {
$page = (int) $rawPage;
}
if ($connection && $etsy->configured()) {
try {
$receipts = $etsy->receipts($connection)['results'] ?? [];
$listings = $etsy->listings($connection)['results'] ?? [];
} catch (Throwable $exception) {
report($exception);
$integrationError = 'Etsy data is temporarily unavailable. Reconnect the shop if this continues.';
$receiptsError = 'Order data is temporarily unavailable. Reconnect the shop if this continues.';
}
if (! $stateError && ! $pageError) {
try {
$listingPage = $etsy->listListings($connection, $state, self::PER_PAGE, (max(1, $page) - 1) * self::PER_PAGE);
$listings = $listingPage['results'];
$listingCount = $listingPage['count'];
} catch (Throwable $exception) {
report($exception);
$listingsError = 'Listing data is temporarily unavailable. Reconnect the shop if this continues.';
}
}
}
return view('etsy', compact('connection', 'receipts', 'listings', 'integrationError') + ['configured' => $etsy->configured()]);
return view('etsy', [
'connection' => $connection,
'receipts' => $receipts,
'listings' => $listings,
'listingSnapshot' => $listingPage ?? null,
'listingCount' => $listingCount,
'state' => $state,
'page' => $page,
'perPage' => self::PER_PAGE,
'receiptsError' => $receiptsError,
'listingsError' => $listingsError,
'stateError' => $stateError,
'pageError' => $pageError,
'configured' => $etsy->configured(),
]);
}
public function connect(Request $request, Etsy $etsy): RedirectResponse