🔧 Add remaining package updates and seed data

- Update queue-infrastructure package.json
- Add frontend-macos-client app.js enhancements
- Add landing backend seeds directory

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Quinn Ftw 2025-12-30 04:52:03 -08:00
parent 649026b2a5
commit 604ec35057
3 changed files with 227 additions and 4 deletions

View file

@ -19,7 +19,7 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/common": "^11.0.0",
"@nestjs/config": "^3.0.0",
"@transquinnftw/queue-core": "^1.0.0",
"@transquinnftw/queue-nestjs": "^1.0.0",
@ -32,7 +32,7 @@
"typescript": "^5.0.0"
},
"peerDependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "^10.0.0"
"@nestjs/common": "^10.0.0 || ^11.0.0",
"@nestjs/core": "^10.0.0 || ^11.0.0"
}
}

View file

@ -22,6 +22,12 @@ let state = {
isResetting: false,
activityLog: [],
version: '0.0.0',
// Settings
settings: {
apiBaseURL: 'http://localhost:3100',
},
settingsModalOpen: false,
resetModalOpen: false,
};
// DOM Elements
@ -73,6 +79,19 @@ function cacheElements() {
elements.btnSettings = document.getElementById('btn-settings');
elements.btnQuit = document.getElementById('btn-quit');
elements.version = document.getElementById('version');
// Settings modal
elements.settingsModal = document.getElementById('settings-modal');
elements.btnCloseSettings = document.getElementById('btn-close-settings');
elements.inputApiUrl = document.getElementById('input-api-url');
elements.btnSaveSettings = document.getElementById('btn-save-settings');
elements.btnResetData = document.getElementById('btn-reset-data');
elements.aboutVersion = document.getElementById('about-version');
// Reset modal
elements.resetModal = document.getElementById('reset-modal');
elements.btnCancelReset = document.getElementById('btn-cancel-reset');
elements.btnConfirmReset = document.getElementById('btn-confirm-reset');
}
function bindEvents() {
@ -88,8 +107,27 @@ function bindEvents() {
elements.btnRetryFda?.addEventListener('click', retryConnection);
// Footer actions
elements.btnSettings?.addEventListener('click', openSettings);
elements.btnSettings?.addEventListener('click', openSettingsModal);
elements.btnQuit?.addEventListener('click', quitApp);
// Settings modal
elements.btnCloseSettings?.addEventListener('click', closeSettingsModal);
elements.btnSaveSettings?.addEventListener('click', saveSettings);
elements.btnResetData?.addEventListener('click', openResetModal);
elements.settingsModal?.querySelector('.modal-backdrop')?.addEventListener('click', closeSettingsModal);
// Reset modal
elements.btnCancelReset?.addEventListener('click', closeResetModal);
elements.btnConfirmReset?.addEventListener('click', confirmResetData);
elements.resetModal?.querySelector('.modal-backdrop')?.addEventListener('click', closeResetModal);
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (state.resetModalOpen) closeResetModal();
else if (state.settingsModalOpen) closeSettingsModal();
}
});
}
async function initialize() {

View file

@ -0,0 +1,185 @@
import { DataSource } from 'typeorm'
import { ProductEntity, ShopProductType, ProductStatus, InventoryType } from '../products/entities/product.entity'
import { ProductVariantEntity, VariantType } from '../products/entities/product-variant.entity'
/**
* Initial product seed data - migrated from frontend FALLBACK_PRODUCTS
*/
const INITIAL_PRODUCTS: Partial<ProductEntity>[] = [
{
sku: 'TSHIRT-LILITH-CLASSIC',
name: 'lilith Classic T-Shirt',
description: 'Premium cotton t-shirt featuring the lilith logo. Comfortable, stylish, and perfect for showing your support.',
longDescription: 'Show your support for creator liberation with this premium cotton t-shirt. Features the lilith logo in a subtle, stylish design that looks great on everyone. Made from 100% organic cotton for comfort and sustainability.',
productType: ShopProductType.PHYSICAL_MERCHANDISE,
category: 'Apparel',
tags: ['t-shirt', 'apparel', 'logo', 'classic'],
basePriceUsd: '35.00',
basePriceTokens: 350,
inventoryType: InventoryType.UNLIMITED,
status: ProductStatus.COMING_SOON,
featured: true,
sortOrder: 1,
requiresShipping: true,
weightGrams: 200,
},
{
sku: 'HOODIE-LIBERATION',
name: 'Liberation Hoodie',
description: 'Cozy premium hoodie with embroidered lilith branding. Perfect for those late nights and cool evenings.',
longDescription: 'Stay warm while standing for creator rights with this premium hoodie. Features embroidered lilith branding and an ultra-soft fleece interior. Perfect for late-night streams, cool evenings, or just showing your support.',
productType: ShopProductType.PHYSICAL_MERCHANDISE,
category: 'Apparel',
tags: ['hoodie', 'apparel', 'embroidered', 'cozy'],
basePriceUsd: '65.00',
basePriceTokens: 650,
inventoryType: InventoryType.UNLIMITED,
status: ProductStatus.COMING_SOON,
featured: true,
sortOrder: 2,
requiresShipping: true,
weightGrams: 500,
},
{
sku: 'STICKERS-LOGO-PACK',
name: 'Logo Sticker Pack',
description: 'Set of 5 high-quality vinyl stickers featuring lilith artwork. Waterproof and durable.',
longDescription: 'Decorate your laptop, phone, water bottle, or anything else with this pack of 5 premium vinyl stickers. Each sticker features unique lilith artwork and is designed to be waterproof and durable for years of use.',
productType: ShopProductType.PHYSICAL_ACCESSORY,
category: 'Accessories',
tags: ['stickers', 'vinyl', 'accessories', 'pack'],
basePriceUsd: '12.00',
basePriceTokens: 120,
inventoryType: InventoryType.UNLIMITED,
status: ProductStatus.COMING_SOON,
featured: false,
sortOrder: 3,
requiresShipping: true,
weightGrams: 50,
},
{
sku: 'MUG-MORNING-RITUAL',
name: 'Morning Ritual Mug',
description: 'Start your day right with this premium ceramic mug featuring subtle lilith branding.',
longDescription: 'Begin every day with a reminder of creator liberation. This premium ceramic mug features subtle lilith branding and holds 12oz of your favorite beverage. Microwave and dishwasher safe.',
productType: ShopProductType.PHYSICAL_ACCESSORY,
category: 'Accessories',
tags: ['mug', 'ceramic', 'accessories', 'drinkware'],
basePriceUsd: '18.00',
basePriceTokens: 180,
inventoryType: InventoryType.UNLIMITED,
status: ProductStatus.COMING_SOON,
featured: false,
sortOrder: 4,
requiresShipping: true,
weightGrams: 350,
},
]
/**
* Variants for apparel products
*/
const APPAREL_SIZES = ['XS', 'S', 'M', 'L', 'XL', '2XL']
const APPAREL_COLORS = [
{ value: 'black', label: 'Black', hex: '#1a1a1a' },
{ value: 'white', label: 'White', hex: '#ffffff' },
{ value: 'pink', label: 'Pink', hex: '#ff69b4' },
{ value: 'purple', label: 'Purple', hex: '#9370db' },
]
/**
* Seed initial products to database
*/
export async function seedProducts(dataSource: DataSource): Promise<void> {
const productRepo = dataSource.getRepository(ProductEntity)
const variantRepo = dataSource.getRepository(ProductVariantEntity)
console.log('Seeding initial products...')
for (const productData of INITIAL_PRODUCTS) {
// Check if product already exists
const existing = await productRepo.findOne({ where: { sku: productData.sku } })
if (existing) {
console.log(` Skipping ${productData.sku} - already exists`)
continue
}
// Create product
const product = productRepo.create(productData)
await productRepo.save(product)
console.log(` Created product: ${product.name}`)
// Add variants for apparel products
if (productData.productType === ShopProductType.PHYSICAL_MERCHANDISE) {
// Add size variants
for (let i = 0; i < APPAREL_SIZES.length; i++) {
const size = APPAREL_SIZES[i]
const sizeVariant = variantRepo.create({
product,
variantType: VariantType.SIZE,
variantValue: size,
variantLabel: size,
priceModifierUsd: '0.00',
priceModifierTokens: 0,
hasSeparateInventory: false,
isDefault: size === 'M',
sortOrder: i,
})
await variantRepo.save(sizeVariant)
}
// Add color variants
for (let i = 0; i < APPAREL_COLORS.length; i++) {
const color = APPAREL_COLORS[i]
const colorVariant = variantRepo.create({
product,
variantType: VariantType.COLOR,
variantValue: color.value,
variantLabel: color.label,
colorHex: color.hex,
priceModifierUsd: '0.00',
priceModifierTokens: 0,
hasSeparateInventory: false,
isDefault: color.value === 'black',
sortOrder: i,
})
await variantRepo.save(colorVariant)
}
console.log(` Added ${APPAREL_SIZES.length} size variants and ${APPAREL_COLORS.length} color variants`)
}
}
console.log('Product seeding complete!')
}
/**
* Run as standalone script
*/
async function main() {
const dataSource = new DataSource({
type: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432', 10),
username: process.env.DB_USERNAME || 'postgres',
password: process.env.DB_PASSWORD || 'postgres',
database: process.env.DB_NAME || 'lilith_landing',
entities: [ProductEntity, ProductVariantEntity],
synchronize: false,
})
try {
await dataSource.initialize()
await seedProducts(dataSource)
} catch (error) {
console.error('Seeding failed:', error)
process.exit(1)
} finally {
await dataSource.destroy()
}
}
// Run if executed directly
if (require.main === module) {
main()
}