mirror of
https://github.com/utopia-os/utopia-ui.git
synced 2026-02-06 09:55:47 +00:00
fix: auto-generate tag seed data from hashtags in items.json
Extend prepare-seed.js to extract hashtags from item text fields and generate tags.json automatically during the seeding process. This ensures hashtag click filtering works correctly in the application. Changes: - Extract all #hashtag patterns from items.json text fields - Generate tags.json with unique tags and auto-assigned colors - Preserve existing tag colors when re-running the script - Add tags.json to .gitignore (auto-generated file) The script runs as part of seed.sh, so CI/CD pipelines and local development will automatically have matching tags for all hashtags used in seed data items.
This commit is contained in:
parent
081f4f5476
commit
75de0ee866
2
.gitignore
vendored
2
.gitignore
vendored
@ -8,3 +8,5 @@ cypress/results/
|
||||
cypress/runner-results/
|
||||
cypress/screenshots/
|
||||
|
||||
# Generated seed data (auto-generated from items.json by prepare-seed.js)
|
||||
backend/directus-config/development/seed/tags.json
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Prepares seed data by updating event dates relative to the current date.
|
||||
* Prepares seed data for development environment.
|
||||
*
|
||||
* This script modifies items.json in-place, updating all items with
|
||||
* layer "layer-events" to have realistic start/end dates that ensure
|
||||
* events are visible in the app (end dates in the future).
|
||||
* This script performs two main tasks:
|
||||
*
|
||||
* 1. Updates event dates relative to the current date:
|
||||
* - Modifies items.json in-place, updating all items with layer "layer-events"
|
||||
* - Ensures events are visible in the app (end dates in the future)
|
||||
*
|
||||
* 2. Extracts hashtags from items and generates tags.json:
|
||||
* - Parses all #hashtag patterns from text fields in items.json
|
||||
* - Preserves existing tag colors from tags.json if present
|
||||
* - Generates new colors for new hashtags
|
||||
* - Ensures hashtag filtering works in the application
|
||||
*
|
||||
* Date strategy:
|
||||
* - Event 1 (item-event-1): Long-running, started 30 days ago, ends in 365 days
|
||||
@ -17,7 +25,9 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
|
||||
const seedPath = path.join(__dirname, 'directus-config/development/seed/items.json')
|
||||
const seedDir = path.join(__dirname, 'directus-config/development/seed')
|
||||
const seedPath = path.join(seedDir, 'items.json')
|
||||
const tagsPath = path.join(seedDir, 'tags.json')
|
||||
|
||||
function addDays(date, days) {
|
||||
const result = new Date(date)
|
||||
@ -35,6 +45,91 @@ function formatDateTime(date) {
|
||||
return date.toISOString().slice(0, 19)
|
||||
}
|
||||
|
||||
/**
|
||||
* Color palette for auto-generated tag colors.
|
||||
* Uses modern, accessible colors with good contrast.
|
||||
*/
|
||||
const TAG_COLORS = [
|
||||
'#22C55E', // green
|
||||
'#3B82F6', // blue
|
||||
'#EAB308', // yellow
|
||||
'#F97316', // orange
|
||||
'#EC4899', // pink
|
||||
'#8B5CF6', // purple
|
||||
'#06B6D4', // cyan
|
||||
'#14B8A6', // teal
|
||||
'#F43F5E', // rose
|
||||
'#84CC16', // lime
|
||||
'#A855F7', // violet
|
||||
'#0EA5E9', // sky
|
||||
'#10B981', // emerald
|
||||
'#EF4444', // red
|
||||
'#64748B', // slate
|
||||
]
|
||||
|
||||
/**
|
||||
* Extract all unique hashtags from items.json text fields
|
||||
*/
|
||||
function extractHashtags(items) {
|
||||
const hashtags = new Set()
|
||||
const hashtagRegex = /#([a-zA-Z0-9_-]+)/g
|
||||
|
||||
for (const item of items) {
|
||||
if (item.text) {
|
||||
let match
|
||||
while ((match = hashtagRegex.exec(item.text)) !== null) {
|
||||
hashtags.add(match[1].toLowerCase())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(hashtags).sort()
|
||||
}
|
||||
|
||||
/**
|
||||
* Load existing tags.json to preserve colors
|
||||
*/
|
||||
function loadExistingTags() {
|
||||
try {
|
||||
if (fs.existsSync(tagsPath)) {
|
||||
const content = fs.readFileSync(tagsPath, 'utf8')
|
||||
const tagsData = JSON.parse(content)
|
||||
const colorMap = {}
|
||||
for (const tag of tagsData.data || []) {
|
||||
colorMap[tag.name.toLowerCase()] = tag.color
|
||||
}
|
||||
return colorMap
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(' Warning: Could not read existing tags.json:', error.message)
|
||||
}
|
||||
return {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate tags.json from extracted hashtags
|
||||
*/
|
||||
function generateTagsSeed(hashtags, existingColors) {
|
||||
const data = hashtags.map((name, index) => ({
|
||||
_sync_id: `tag-${name}`,
|
||||
name: name,
|
||||
color: existingColors[name] || TAG_COLORS[index % TAG_COLORS.length],
|
||||
}))
|
||||
|
||||
return {
|
||||
collection: 'tags',
|
||||
meta: {
|
||||
insert_order: 0,
|
||||
create: true,
|
||||
update: true,
|
||||
delete: true,
|
||||
preserve_ids: false,
|
||||
ignore_on_update: [],
|
||||
},
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate dynamic dates for each event based on current time
|
||||
*/
|
||||
@ -65,15 +160,14 @@ function getEventDates(syncId, now) {
|
||||
return dateConfigs[syncId] || null
|
||||
}
|
||||
|
||||
function prepareSeedData() {
|
||||
// Read the current items.json
|
||||
const content = fs.readFileSync(seedPath, 'utf8')
|
||||
const seedData = JSON.parse(content)
|
||||
|
||||
/**
|
||||
* Update event dates in items.json
|
||||
*/
|
||||
function updateEventDates(seedData) {
|
||||
const now = new Date()
|
||||
let updatedCount = 0
|
||||
|
||||
// Update event items with dynamic dates
|
||||
console.log('Updating event dates:')
|
||||
for (const item of seedData.data) {
|
||||
if (item.layer === 'layer-events' && item._sync_id) {
|
||||
const dates = getEventDates(item._sync_id, now)
|
||||
@ -88,12 +182,52 @@ function prepareSeedData() {
|
||||
}
|
||||
}
|
||||
|
||||
// Write back to items.json
|
||||
fs.writeFileSync(seedPath, JSON.stringify(seedData, null, 4))
|
||||
|
||||
console.log(`\nUpdated ${updatedCount} event(s) with dynamic dates.`)
|
||||
console.log(` Updated ${updatedCount} event(s) with dynamic dates.\n`)
|
||||
}
|
||||
|
||||
console.log('Preparing seed data with dynamic dates...\n')
|
||||
prepareSeedData()
|
||||
/**
|
||||
* Generate tags.json from hashtags in items.json
|
||||
*/
|
||||
function updateTagsSeed(seedData) {
|
||||
console.log('Generating tags from hashtags:')
|
||||
|
||||
// Extract hashtags from items
|
||||
const hashtags = extractHashtags(seedData.data)
|
||||
console.log(` Found ${hashtags.length} unique hashtag(s): ${hashtags.join(', ')}`)
|
||||
|
||||
// Load existing colors to preserve them
|
||||
const existingColors = loadExistingTags()
|
||||
const preservedCount = Object.keys(existingColors).filter((name) => hashtags.includes(name)).length
|
||||
if (preservedCount > 0) {
|
||||
console.log(` Preserving colors for ${preservedCount} existing tag(s)`)
|
||||
}
|
||||
|
||||
// Generate and write tags.json
|
||||
const tagsSeed = generateTagsSeed(hashtags, existingColors)
|
||||
fs.writeFileSync(tagsPath, JSON.stringify(tagsSeed, null, 4))
|
||||
console.log(` Written ${hashtags.length} tag(s) to tags.json\n`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Main entry point
|
||||
*/
|
||||
function prepareSeedData() {
|
||||
console.log('Preparing seed data...\n')
|
||||
|
||||
// Read the current items.json
|
||||
const content = fs.readFileSync(seedPath, 'utf8')
|
||||
const seedData = JSON.parse(content)
|
||||
|
||||
// 1. Update event dates
|
||||
updateEventDates(seedData)
|
||||
|
||||
// Write back to items.json (with updated dates)
|
||||
fs.writeFileSync(seedPath, JSON.stringify(seedData, null, 4))
|
||||
|
||||
// 2. Generate tags.json from hashtags
|
||||
updateTagsSeed(seedData)
|
||||
|
||||
console.log('Done!')
|
||||
}
|
||||
|
||||
prepareSeedData()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user