1import { eveT } from '../../i18n/i18n.js'; 2import { openPanelSurface } from '../../intuition/runtime/panel_api.js'; 3import { DASHBOARD_PROJECT_GOALS, dashboardProjectChoiceLabelKey } from './dashboard_creation_catalog.js'; 4import { DEFAULT_NEWS_TEMPLATE_ID, findNewsTemplate } from '../news/news_template_model.js'; 5 6export const dashboardProjectChoiceLabel = (family, goal = '') => eveT(dashboardProjectChoiceLabelKey(family, goal), goal || family); 7 8// Une News est un PROJET (le fil, ouvert en mode liste) doublé d'une carte Dashboard : 9// la creation vit donc dans le domaine News, pas dans l'API projet generique. 10const loadNewsThreadCreator = async () => { 11 const module = await import('../news/news_thread_runtime.js'); 12 if (typeof module?.newsThreadRuntime?.createNewsThread !== 'function') { 13 throw new Error('dashboard_creation_news_create_unavailable'); 14 } 15 return module.newsThreadRuntime.createNewsThread; 16}; 17 18const loadProjectCreator = async () => { 19 const module = await import('../../intuition/matrix/core/project_data.js'); 20 if (typeof module?.createProjectRecord !== 'function') throw new Error('dashboard_creation_project_create_unavailable'); 21 return module.createProjectRecord; 22}; 23 24// Les createurs derriere les cellules de creation du Dashboard. Le choix (quelle 25// cellule, quelle etape) appartient a `dashboard_creation_runtime.js` ; ici on ne fait 26// que creer puis OUVRIR, par le meme chemin qu'une tuile tapee. 27export const createDashboardCreationActions = ({ 28 invalidateCategories, 29 openPanel = openPanelSurface, 30 projectCreator = loadProjectCreator, 31 newsCreator = loadNewsThreadCreator, 32 openProject = null 33} = {}) => { 34 if (typeof invalidateCategories !== 'function') throw new Error('dashboard_creation_invalidator_required'); 35 if (typeof openPanel !== 'function') throw new Error('dashboard_creation_panel_opener_required'); 36 if (typeof projectCreator !== 'function') throw new Error('dashboard_creation_project_creator_required'); 37 if (typeof newsCreator !== 'function') throw new Error('dashboard_creation_news_creator_required'); 38 if (typeof openProject !== 'function') throw new Error('dashboard_creation_project_opener_required'); 39 40 // Une News EST un projet : la creation se contente de poser le tag, puis d'OUVRIR le 41 // projet par le meme chemin qu'une tuile tapee. Activer le workspace ici, sans la 42 // transition de mode du Dashboard, laissait la vue sur l'ancien projet. 43 // 44 // `template` decide de la forme : le mode de vue voyage dans la presentation (relue par 45 // `restoreProjectViewMode`), les types de contenu dans les proprietes du projet â la 46 // presentation jette silencieusement les cles qu'elle ne declare pas. 47 const createNews = async ({ template = null } = {}) => { 48 const createdOrCreator = await newsCreator(); 49 const args = template 50 ? { 51 presentation: { view_mode: template.view_mode }, 52 properties: { news_template: { ...template } } 53 } 54 : {}; 55 const created = typeof createdOrCreator === 'function' ? await createdOrCreator(args) : createdOrCreator; 56 if (created?.ok !== true) throw new Error(created?.error || 'dashboard_creation_news_create_failed'); 57 // Le meme objet apparait sur les deux rangees : les deux doivent etre rechargees. 58 await invalidateCategories(['news', 'projects']); 59 const project = created.project || { id: created.project_id, name: created.project_name }; 60 const opened = await openProject({ payload: project }); 61 if (opened?.ok === false) throw new Error(opened.error || 'dashboard_creation_news_open_failed'); 62 return { ok: true, category_id: 'news', created, opened }; 63 }; 64 65 const createGuidedProject = async ({ family, goal } = {}) => { 66 if (!DASHBOARD_PROJECT_GOALS[family]?.includes(goal)) throw new Error('dashboard_project_goal_invalid'); 67 const creator = await (family === 'publication' ? newsCreator : projectCreator)(); 68 const name = dashboardProjectChoiceLabel(family, goal); 69 const intent = { family, goal, ...(family === 'publication' ? { status: 'draft', audience: goal } : {}) }; 70 const created = typeof creator === 'function' ? await creator({ name, empty: true, properties: { project_intent: intent } }) : creator; 71 if (created?.ok === false) throw new Error(created.error || 'dashboard_guided_create_failed'); 72 const project = created.project || { id: created.createdId || created.project_id, name }; 73 if (!project.id) throw new Error('dashboard_guided_project_id_required'); 74 await invalidateCategories(family === 'publication' ? ['projects', 'news'] : ['projects']); 75 const opened = await openProject({ payload: project }); 76 if (opened?.ok === false) throw new Error(opened.error || 'dashboard_guided_open_failed'); 77 return { ok: true, project, intent, opened }; 78 }; 79 80 // Appui long sur « Nouveau » : un projet vierge au nom par defaut (le createur 81 // attribue le suivant), ou une News au template le plus simple, puis OUVERTURE par 82 // le meme chemin qu'une tuile tapee. 83 const createDefaultProject = async () => { 84 const creator = await projectCreator(); 85 const created = typeof creator === 'function' ? await creator({ name: '', empty: true, properties: {} }) : creator; 86 if (created?.ok === false) throw new Error(created.error || 'dashboard_default_project_create_failed'); 87 const project = created.project || { id: created.createdId || created.project_id, name: created.projectName || created.project_name }; 88 if (!project.id) throw new Error('dashboard_default_project_id_required'); 89 await invalidateCategories(['projects']); 90 const opened = await openProject({ payload: project }); 91 if (opened?.ok === false) throw new Error(opened.error || 'dashboard_default_project_open_failed'); 92 return { ok: true, category_id: 'projects', project, opened }; 93 }; 94 95 const createDefault = async (categoryId = '') => { 96 if (categoryId === 'projects') return createDefaultProject(); 97 if (categoryId === 'news') { 98 const template = findNewsTemplate(DEFAULT_NEWS_TEMPLATE_ID); 99 return createNews({ template: template ? { id: template.id, view_mode: template.view_mode, content: [...template.content] } : null }); 100 } 101 throw new Error(`dashboard_creation_default_unsupported:${categoryId}`); 102 }; 103 104 const openEditor = async (surfaceKey, projectId = null) => {
105 const result = await openPanel(surfaceKey, { 106 createNew: true, projectId, source: { type: 'dashboard_header_create' } 107 }); 108 if (result?.ok !== true) throw new Error(result?.error || 'dashboard_creation_panel_open_failed'); 109 return { ok: true, surface: surfaceKey, opened: true }; 110 }; 111 112 return { createGuidedProject, createNews, createDefault, openEditor }; 113};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.