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 {makeT} from 'app/client/lib/localization';
import {logTelemetryEvent} from 'app/client/lib/telemetry';
import {DocumentType} from 'app/common/UserAPI';
// tslint:disable:no-console
@ -87,7 +88,7 @@ export interface DocPageModel {
isTutorialTrunk: Observable<boolean>;
isTutorialFork: Observable<boolean>;
isTemplate: Observable<boolean>;
type: Observable<DocumentType|null>;
importSources: ImportSource[];
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);
public readonly isTemplate = Computed.create(this, this.currentDoc,
(use, doc) => doc ? doc.isTemplate : false);
public readonly type = Computed.create(this, this.currentDoc,
(use, doc) => doc?.type?? null);
public readonly importSources: ImportSource[] = [];
@ -499,7 +502,8 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
const isFork = Boolean(idParts.forkId || idParts.snapshotId);
const isBareFork = isFork && idParts.trunkId === NEW_DOCUMENT_CODE;
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 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
// to set it here, as it'll potentially be confusing for other code reading it.
openMode = 'default';
} else if (!isFork && doc.type === 'template') {
} else if (!isFork && type === 'template') {
// Templates should always open in fork mode by default.
openMode = 'fork';
} else {
@ -521,7 +525,7 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
}
const isPreFork = openMode === 'fork';
const isTemplate = doc.type === 'template' && (isFork || isPreFork);
const isTemplate = type === 'template' && (isFork || isPreFork);
const isEditable = !isSnapshot && (canEdit(doc.access) || isPreFork);
return {
...doc,
@ -534,6 +538,7 @@ function buildDocInfo(doc: Document, mode: OpenDocMode | undefined): DocInfo {
isSnapshot,
isTutorialTrunk,
isTutorialFork,
type,
isTemplate,
isReadonly: !isEditable,
idParts,

View File

@ -31,6 +31,7 @@ import {getCurrency, locales} from 'app/common/Locales';
import {isOwner, isOwnerOrEditor} from 'app/common/roles';
import {Computed, Disposable, dom, fromKo, IDisposableOwner, makeTestId, Observable, styled} from 'grainjs';
import * as moment from 'moment-timezone';
import {DocumentType} from 'app/common/UserAPI';
const t = makeT('DocumentSettings');
const testId = makeTestId('test-settings-');
@ -41,6 +42,7 @@ export class DocSettingsPage extends Disposable {
private _timezone = this._docInfo.timezone;
private _locale: KoSaveableObservable<string> = this._docInfo.documentSettingsJson.prop('locale');
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, (
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'})),
}),
]),
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) {
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', `
overflow-y: auto;
position: relative;