PageSourceSearch

https://atome.one/eVe/domains/dashboard/dashboard_creation_actions.js

js atome.one collected 2026-10-04 22:58:02 UTC 7,016 bytes, 113 lines download raw bytes

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.