first functionnal POC

Co-authored-by: hexaltation <gregoire@cutzach.com>
Co-authored-by: fflorent <florent.git@zeteo.me>
This commit is contained in:
fflorent 2024-06-12 10:59:26 +02:00 committed by Grégoire Cutzach
parent e9b5b98bcb
commit f40bd9d075
No known key found for this signature in database
GPG Key ID: AA4155BE23C375E6
2 changed files with 67 additions and 5 deletions

View File

@ -29,6 +29,7 @@ import {Holder, Observable, subscribe} from 'grainjs';
import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs'; import {Computed, Disposable, dom, DomArg, DomElementArg} from 'grainjs';
import {makeT} from 'app/client/lib/localization'; import {makeT} from 'app/client/lib/localization';
import {logTelemetryEvent} from 'app/client/lib/telemetry'; import {logTelemetryEvent} from 'app/client/lib/telemetry';
import {DocumentType} from 'app/common/UserAPI';
// tslint:disable:no-console // tslint:disable:no-console
@ -87,7 +88,7 @@ export interface DocPageModel {
isTutorialTrunk: Observable<boolean>; isTutorialTrunk: Observable<boolean>;
isTutorialFork: Observable<boolean>; isTutorialFork: Observable<boolean>;
isTemplate: Observable<boolean>; isTemplate: Observable<boolean>;
type: Observable<DocumentType|null>;
importSources: ImportSource[]; importSources: ImportSource[];
undoState: Observable<IUndoState|null>; // See UndoStack for details. undoState: Observable<IUndoState|null>; // See UndoStack for details.
@ -147,6 +148,8 @@ export class DocPageModelImpl extends Disposable implements DocPageModel {
(use, doc) => doc ? doc.isTutorialFork : false); (use, doc) => doc ? doc.isTutorialFork : false);
public readonly isTemplate = Computed.create(this, this.currentDoc, public readonly isTemplate = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isTemplate : false); (use, doc) => doc ? doc.isTemplate : false);
public readonly type = Computed.create(this, this.currentDoc,
(use, doc) => doc?.type?? null);
public readonly importSources: ImportSource[] = []; public readonly importSources: ImportSource[] = [];
@ -499,7 +502,8 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
const isFork = Boolean(idParts.forkId || idParts.snapshotId); const isFork = Boolean(idParts.forkId || idParts.snapshotId);
const isBareFork = isFork && idParts.trunkId === NEW_DOCUMENT_CODE; const isBareFork = isFork && idParts.trunkId === NEW_DOCUMENT_CODE;
const isSnapshot = Boolean(idParts.snapshotId); const isSnapshot = Boolean(idParts.snapshotId);
const isTutorial = doc.type === 'tutorial'; const type = doc.type;
const isTutorial = type === 'tutorial';
const isTutorialTrunk = isTutorial && !isFork && mode !== 'default'; const isTutorialTrunk = isTutorial && !isFork && mode !== 'default';
const isTutorialFork = isTutorial && isFork; const isTutorialFork = isTutorial && isFork;
@ -511,7 +515,7 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
// mode. Since the document's 'openMode' has no effect, don't bother trying // mode. Since the document's 'openMode' has no effect, don't bother trying
// to set it here, as it'll potentially be confusing for other code reading it. // to set it here, as it'll potentially be confusing for other code reading it.
openMode = 'default'; openMode = 'default';
} else if (!isFork && doc.type === 'template') { } else if (!isFork && type === 'template') {
// Templates should always open in fork mode by default. // Templates should always open in fork mode by default.
openMode = 'fork'; openMode = 'fork';
} else { } else {
@ -521,7 +525,7 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
} }
const isPreFork = openMode === 'fork'; const isPreFork = openMode === 'fork';
const isTemplate = doc.type === 'template' && (isFork || isPreFork); const isTemplate = type === 'template' && (isFork || isPreFork);
const isEditable = !isSnapshot && (canEdit(doc.access) || isPreFork); const isEditable = !isSnapshot && (canEdit(doc.access) || isPreFork);
return { return {
...doc, ...doc,
@ -534,6 +538,7 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
isSnapshot, isSnapshot,
isTutorialTrunk, isTutorialTrunk,
isTutorialFork, isTutorialFork,
type,
isTemplate, isTemplate,
isReadonly: !isEditable, isReadonly: !isEditable,
idParts, idParts,

View File

@ -31,6 +31,7 @@ import {getCurrency, locales} from 'app/common/Locales';
import {isOwner, isOwnerOrEditor} from 'app/common/roles'; import {isOwner, isOwnerOrEditor} from 'app/common/roles';
import {Computed, Disposable, dom, fromKo, IDisposableOwner, makeTestId, Observable, styled} from 'grainjs'; import {Computed, Disposable, dom, fromKo, IDisposableOwner, makeTestId, Observable, styled} from 'grainjs';
import * as moment from 'moment-timezone'; import * as moment from 'moment-timezone';
import {DocumentType} from 'app/common/UserAPI';
const t = makeT('DocumentSettings'); const t = makeT('DocumentSettings');
const testId = makeTestId('test-settings-'); const testId = makeTestId('test-settings-');
@ -41,6 +42,7 @@ export class DocSettingsPage extends Disposable {
private _timezone = this._docInfo.timezone; private _timezone = this._docInfo.timezone;
private _locale: KoSaveableObservable<string> = this._docInfo.documentSettingsJson.prop('locale'); private _locale: KoSaveableObservable<string> = this._docInfo.documentSettingsJson.prop('locale');
private _currency: KoSaveableObservable<string|undefined> = this._docInfo.documentSettingsJson.prop('currency'); private _currency: KoSaveableObservable<string|undefined> = this._docInfo.documentSettingsJson.prop('currency');
// private _type: KoSaveableObservable<string|undefined> = this._docInfo.documentSettingsJson.prop('type');
private _engine: Computed<EngineCode|undefined> = Computed.create(this, ( private _engine: Computed<EngineCode|undefined> = Computed.create(this, (
use => use(this._docInfo.documentSettingsJson.prop('engine')) use => use(this._docInfo.documentSettingsJson.prop('engine'))
)) ))
@ -194,6 +196,14 @@ export class DocSettingsPage extends Disposable {
value: cssSmallLinkButton(t('Manage webhooks'), urlState().setLinkUrl({docPage: 'webhook'})), value: cssSmallLinkButton(t('Manage webhooks'), urlState().setLinkUrl({docPage: 'webhook'})),
}), }),
]), ]),
dom.create(AdminSection, t('Document conversion'), [
dom.create(AdminSectionItem, {
id: 'document-type',
name: t('Document type'),
description: t('Convert the document'),
value: dom.create(buildTypeSelect, docPageModel.type, docPageModel.currentDocId.get()),
}),
]),
); );
} }
@ -298,7 +308,15 @@ export class DocSettingsPage extends Disposable {
} }
} }
function persistType(type: string|null, docId: string|undefined){
docId = docId?.split("~")[0];
return fetch(`/o/docs/api/docs/${docId}`,
{ method:'PATCH',
headers: {"Content-Type": "application/json"},
credentials: 'include',
body:JSON.stringify({type})
}).catch((err)=>{ console.log(err); });
}
function getApiConsoleLink(docPageModel: DocPageModel) { function getApiConsoleLink(docPageModel: DocPageModel) {
const url = new URL(location.href); const url = new URL(location.href);
@ -343,6 +361,45 @@ function buildLocaleSelect(
); );
} }
type DocumentTypeItem = ACSelectItem & {type?: string};
function buildTypeSelect(
owner: IDisposableOwner,
type: Observable<DocumentType|null>,
id: string|undefined,
) {
const typeList: DocumentTypeItem[] = [{
label: t('Regular'),
type: ''
}, {
label: t('Template'),
type: 'template'
},
{
label: t('Tutorial'),
type: 'tutorial'
}].map((el) => ({
...el,
value: el.label,
cleanText: el.label.trim().toLowerCase()
}));
const typeObs = Computed.create(owner, use => {
const typeCode = use(type)??"";
const typeName = typeList.find(ty => ty.type === typeCode)?.label || typeCode;
return typeName;
});
const acIndex = new ACIndexImpl<DocumentTypeItem>(typeList, {maxResults: 200, keepOrder: true});
return buildACSelect(owner, {
acIndex, valueObs: typeObs,
save(_value, item: DocumentTypeItem | undefined) {
if (!item) { throw new Error("Invalid DocumentType"); }
persistType(item.type!, id)
.then(()=>window.location.reload())
.catch(err=>console.log(err));
}
});
}
const cssContainer = styled('div', ` const cssContainer = styled('div', `
overflow-y: auto; overflow-y: auto;
position: relative; position: relative;