update on timeline on portfolio view + some corrections

This commit is contained in:
fahed
2026-02-10 13:20:49 +03:00
parent d15e54044e
commit 334727b232
37 changed files with 5119 additions and 1440 deletions

249
server/setup-tables.js Normal file
View File

@@ -0,0 +1,249 @@
#!/usr/bin/env node
/**
* setup-tables.js — Creates a new "Digital Hub" base in NocoDB
* with all 12 tables, fields, and links.
* Run once: node setup-tables.js
*/
require('dotenv').config({ path: __dirname + '/.env' });
const NOCODB_URL = process.env.NOCODB_URL || 'http://localhost:8090';
const NOCODB_TOKEN = process.env.NOCODB_TOKEN;
const fs = require('fs');
const path = require('path');
async function request(method, url, body) {
const opts = {
method,
headers: { 'xc-token': NOCODB_TOKEN, 'Content-Type': 'application/json' },
};
if (body) opts.body = JSON.stringify(body);
const res = await fetch(url, opts);
if (!res.ok) {
const text = await res.text();
throw new Error(`${method} ${url}${res.status}: ${text}`);
}
const text = await res.text();
return text ? JSON.parse(text) : {};
}
async function createBase() {
console.log('Creating "Digital Hub" base...');
const data = await request('POST', `${NOCODB_URL}/api/v2/meta/bases/`, {
title: 'Digital Hub',
type: 'database',
});
console.log(` Base created: ${data.id}`);
return data.id;
}
async function createTable(baseId, title, columns) {
console.log(` Creating table: ${title}`);
const data = await request('POST', `${NOCODB_URL}/api/v2/meta/bases/${baseId}/tables`, {
title,
columns,
});
console.log(`${data.id}`);
return data;
}
async function createLinkColumn(tableId, title, relatedTableId, type = 'hm') {
console.log(` Linking: ${title}`);
await request('POST', `${NOCODB_URL}/api/v2/meta/tables/${tableId}/columns`, {
title,
uidt: 'Links',
parentId: tableId,
childId: relatedTableId,
type,
});
}
// Field type helpers
const text = (title) => ({ title, uidt: 'SingleLineText' });
const longText = (title) => ({ title, uidt: 'LongText' });
const email = (title) => ({ title, uidt: 'Email' });
const num = (title) => ({ title, uidt: 'Number' });
const decimal = (title) => ({ title, uidt: 'Decimal' });
const checkbox = (title) => ({ title, uidt: 'Checkbox' });
const date = (title) => ({ title, uidt: 'Date' });
const dateTime = (title) => ({ title, uidt: 'DateTime' });
const singleSelect = (title, options) => ({
title,
uidt: 'SingleSelect',
dtxp: options.map(o => `'${o}'`).join(','),
});
async function main() {
try {
// 1. Create base
const baseId = await createBase();
// 2. Create tables (without links first)
const users = await createTable(baseId, 'Users', [
text('name'),
email('email'),
singleSelect('role', ['superadmin', 'manager', 'contributor']),
text('team_role'),
longText('brands'),
text('phone'),
text('avatar'),
checkbox('tutorial_completed'),
]);
const brands = await createTable(baseId, 'Brands', [
text('name'),
text('name_ar'),
num('priority'),
text('color'),
text('icon'),
text('category'),
]);
const campaigns = await createTable(baseId, 'Campaigns', [
text('name'),
longText('description'),
date('start_date'),
date('end_date'),
singleSelect('status', ['planning', 'active', 'paused', 'completed', 'cancelled']),
text('color'),
decimal('budget'),
longText('goals'),
longText('platforms'),
decimal('budget_spent'),
decimal('revenue'),
num('impressions'),
num('clicks'),
num('conversions'),
decimal('cost_per_click'),
longText('notes'),
num('brand_id'),
num('created_by_user_id'),
]);
const campaignTracks = await createTable(baseId, 'CampaignTracks', [
text('name'),
singleSelect('type', ['organic_social', 'paid_social', 'paid_search', 'email', 'seo', 'influencer', 'event', 'other']),
text('platform'),
decimal('budget_allocated'),
decimal('budget_spent'),
decimal('revenue'),
num('impressions'),
num('clicks'),
num('conversions'),
longText('notes'),
singleSelect('status', ['planned', 'active', 'paused', 'completed']),
num('campaign_id'),
]);
const campaignAssignments = await createTable(baseId, 'CampaignAssignments', [
dateTime('assigned_at'),
num('campaign_id'),
num('member_id'),
num('assigner_id'),
]);
const projects = await createTable(baseId, 'Projects', [
text('name'),
longText('description'),
singleSelect('status', ['active', 'paused', 'completed', 'cancelled']),
singleSelect('priority', ['low', 'medium', 'high', 'urgent']),
date('start_date'),
date('due_date'),
num('brand_id'),
num('owner_id'),
num('created_by_user_id'),
]);
const tasks = await createTable(baseId, 'Tasks', [
text('title'),
longText('description'),
singleSelect('status', ['todo', 'in_progress', 'done']),
singleSelect('priority', ['low', 'medium', 'high', 'urgent']),
date('start_date'),
date('due_date'),
checkbox('is_personal'),
dateTime('completed_at'),
num('project_id'),
num('assigned_to_id'),
num('created_by_user_id'),
]);
const posts = await createTable(baseId, 'Posts', [
text('title'),
longText('description'),
singleSelect('status', ['draft', 'in_review', 'approved', 'scheduled', 'published', 'rejected']),
text('platform'),
longText('platforms'),
text('content_type'),
dateTime('scheduled_date'),
dateTime('published_date'),
longText('notes'),
longText('publication_links'),
num('brand_id'),
num('assigned_to_id'),
num('campaign_id'),
num('track_id'),
num('created_by_user_id'),
]);
const assets = await createTable(baseId, 'Assets', [
text('filename'),
text('original_name'),
text('mime_type'),
num('size'),
longText('tags'),
text('folder'),
num('brand_id'),
num('campaign_id'),
num('uploader_id'),
]);
const postAttachments = await createTable(baseId, 'PostAttachments', [
text('filename'),
text('original_name'),
text('mime_type'),
num('size'),
text('url'),
num('post_id'),
]);
const comments = await createTable(baseId, 'Comments', [
text('entity_type'),
num('entity_id'),
longText('content'),
num('user_id'),
]);
const budgetEntries = await createTable(baseId, 'BudgetEntries', [
text('label'),
decimal('amount'),
text('source'),
text('category'),
date('date_received'),
longText('notes'),
num('campaign_id'),
]);
// 3. All relationships are now handled via plain Number FK columns
// (brand_id, owner_id, campaign_id, etc.) defined directly in each table above.
// No NocoDB link columns are needed.
// 4. Save base ID to .env
const envPath = path.join(__dirname, '.env');
let envContent = fs.readFileSync(envPath, 'utf8');
envContent = envContent.replace(/^NOCODB_BASE_ID=.*$/m, `NOCODB_BASE_ID=${baseId}`);
fs.writeFileSync(envPath, envContent);
console.log(`\n✅ Done! Base ID ${baseId} saved to .env`);
console.log('Tables created:');
const tables = [users, brands, campaigns, campaignTracks, campaignAssignments, projects, tasks, posts, assets, postAttachments, comments, budgetEntries];
for (const t of tables) {
console.log(` ${t.title}: ${t.id}`);
}
} catch (err) {
console.error('Setup failed:', err);
process.exit(1);
}
}
main();