Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
164 changes: 149 additions & 15 deletions backend/prepare-seed.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand All @@ -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
*/
Expand Down Expand Up @@ -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)
Expand All @@ -88,12 +182,52 @@ function prepareSeedData() {
}
}

// Write back to items.json
console.log(` Updated ${updatedCount} event(s) with dynamic dates.\n`)
}

/**
* 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))

console.log(`\nUpdated ${updatedCount} event(s) with dynamic dates.`)
// 2. Generate tags.json from hashtags
updateTagsSeed(seedData)

console.log('Done!')
}

console.log('Preparing seed data with dynamic dates...\n')
prepareSeedData()