Skip to content

Commit

Permalink
Frontend Refactor (#86)
Browse files Browse the repository at this point in the history
* chore: target 1.x workflows

* chore: switch from npm to yarn

* chore: update tsconfig

* refactor: use frontend extenders

* chore: use old models

* feat: add shims

* chore: export modules

* chore: start to refactor admin page

* fix: ensure UI is correctly redrawn

* refactor: use ItemList for fields

* refactor: use JSX
  • Loading branch information
DavideIadeluca authored Sep 12, 2024
1 parent dd2ad94 commit 66dafe5
Show file tree
Hide file tree
Showing 19 changed files with 2,655 additions and 6,264 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on: [workflow_dispatch, push, pull_request]

jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@main
uses: flarum/framework/.github/workflows/REUSABLE_backend.yml@1.x
with:
enable_backend_testing: false
enable_phpstan: true
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/frontend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ on: [workflow_dispatch, push, pull_request]

jobs:
run:
uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@main
uses: flarum/framework/.github/workflows/REUSABLE_frontend.yml@1.x
with:
enable_bundlewatch: false
enable_prettier: true
enable_typescript: true

frontend_directory: ./js
backend_directory: .
js_package_manager: npm
js_package_manager: yarn
main_git_branch: master

secrets:
Expand Down
5,922 changes: 0 additions & 5,922 deletions js/package-lock.json

This file was deleted.

3 changes: 2 additions & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"scripts": {
"dev": "webpack --mode development --watch",
"build": "webpack --mode production",
"format": "prettier --write src"
"format": "prettier --write src",
"format-check": "prettier --check src"
},
"devDependencies": {
"@flarum/prettier-config": "^1.0.0",
Expand Down
10 changes: 10 additions & 0 deletions js/src/@types/shims.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type Field from '../lib/models/Field';
import type Answer from '../lib/models/Answer';

declare module 'flarum/common/models/User' {
export default interface User {
bioFields(): Field[];
masqueradeAnswers(): Answer[];
canEditMasqueradeProfile(): boolean;
}
}
230 changes: 230 additions & 0 deletions js/src/admin/components/FieldEdit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import app from 'flarum/admin/app';
import Button from 'flarum/common/components/Button';
import Switch from 'flarum/common/components/Switch';
import Select from 'flarum/common/components/Select';
import withAttr from 'flarum/common/utils/withAttr';
import SelectFieldOptionEditor from './SelectFieldOptionEditor';
import icon from 'flarum/common/helpers/icon';
import ItemList from 'flarum/common/utils/ItemList';

export default class FieldEdit {
view(vnode) {
const { field, loading, onUpdate } = vnode.attrs;
const exists = field.id();

return (
<fieldset className="Field" data-id={field.id()} key={field.id()}>
<legend>
{exists ? (
<Button className="Button Button--icon Button--danger" icon="fas fa-trash" onclick={() => this.deleteField(field, onUpdate)} />
) : null}
<span className="Field-toggle" onclick={(e) => this.toggleField(e)}>
{app.translator.trans('fof-masquerade.admin.fields.' + (exists ? 'edit' : 'add'), {
field: field.name(),
})}
{icon('fas fa-caret-down')}
</span>
</legend>
<div className="Field-body">{this.fieldItems(field, onUpdate).toArray()}</div>
</fieldset>
);
}

fieldItems(field, onUpdate) {
const fields = new ItemList();

fields.add(
'name',
<div className="Form-group">
<label>{app.translator.trans('fof-masquerade.admin.fields.name')}</label>
<input className="FormControl" value={field.name()} oninput={withAttr('value', this.updateExistingFieldInput.bind(this, 'name', field))} />
<span className="helpText">{app.translator.trans('fof-masquerade.admin.fields.name-help')}</span>
</div>,
100
);

fields.add(
'description',
<div className="Form-group">
<label>{app.translator.trans('fof-masquerade.admin.fields.description')}</label>
<input
className="FormControl"
value={field.description()}
oninput={withAttr('value', this.updateExistingFieldInput.bind(this, 'description', field))}
/>
<span className="helpText">{app.translator.trans('fof-masquerade.admin.fields.description-help')}</span>
</div>,
90
);

fields.add(
'icon',
<div className="Form-group">
<label>{app.translator.trans('fof-masquerade.admin.fields.icon')}</label>
<input className="FormControl" value={field.icon()} oninput={withAttr('value', this.updateExistingFieldInput.bind(this, 'icon', field))} />
<span className="helpText">
{app.translator.trans('fof-masquerade.admin.fields.icon-help', {
a: <a href="https://fontawesome.com/icons?m=free" target="_blank" />,
})}
</span>
</div>,
80
);

fields.add(
'on_bio',
<div className="Form-group">
<Switch state={field.on_bio()} onchange={this.updateExistingFieldInput.bind(this, 'on_bio', field)}>
{app.translator.trans('fof-masquerade.admin.fields.on_bio')}
</Switch>
</div>,
70
);

fields.add(
'required',
<div className="Form-group">
<Switch state={field.required()} onchange={this.updateExistingFieldInput.bind(this, 'required', field)}>
{app.translator.trans('fof-masquerade.admin.fields.required')}
</Switch>
</div>,
60
);

fields.add(
'type',
<div className="Form-group">
<label>{app.translator.trans('fof-masquerade.admin.fields.type')}</label>
<Select
onchange={(value) => {
if (value === 'null') {
value = null;
}
this.updateExistingFieldInput('type', field, value);
}}
options={this.availableTypes()}
value={field.type()}
/>
</div>,
50
);

if (field.type() === 'select') {
fields.add(
'select_options',
<SelectFieldOptionEditor
onchange={(value) => {
this.updateExistingFieldInput('validation', field, value);
}}
value={field.validation()}
/>,
40
);
}

if (field.type() === null) {
fields.add(
'validation',
<div className="Form-group">
<label>{app.translator.trans('fof-masquerade.admin.fields.validation')}</label>
<input
className="FormControl"
value={field.validation()}
oninput={withAttr('value', this.updateExistingFieldInput.bind(this, 'validation', field))}
/>
<span className="helpText">
{app.translator.trans('fof-masquerade.admin.fields.validation-help', {
a: <a href="https://laravel.com/docs/5.2/validation#available-validation-rules" target="_blank" />,
})}
</span>
</div>,
30
);
}

fields.add(
'actions',
<div className="Form-group">
<div className="ButtonGroup">
<Button
className="Button Button--primary"
loading={this.loading}
disabled={!this.readyToAdd(field)}
onclick={field.id() ? this.updateExistingField.bind(this, field, onUpdate) : this.submitAddField.bind(this, field, onUpdate)}
>
{app.translator.trans('fof-masquerade.admin.buttons.' + (field.id() ? 'edit' : 'add') + '-field')}
</Button>
{field.id() ? (
<Button className="Button Button--danger" loading={this.loading} onclick={this.deleteField.bind(this, field, onUpdate)}>
{app.translator.trans('fof-masquerade.admin.buttons.delete-field')}
</Button>
) : null}
</div>
</div>,
20
);

return fields;
}

updateExistingFieldInput(what, field, value) {
field.pushAttributes({
[what]: value,
});
}

deleteField(field, onUpdate) {
field.delete().then(onUpdate);
}

toggleField(e) {
$(e.target).parents('.Field').toggleClass('active');
}

submitAddField(field, onUpdate, e) {
e.preventDefault();

field.save(field.data.attributes).then(() => {
onUpdate();
this.resetNewField();
});

m.redraw();
}

updateExistingField(field, onUpdate) {
if (!field.id()) return;

field.save(field.data.attributes).then(onUpdate);
}

resetNewField() {
this.newField = app.store.createRecord('masquerade-field', {
attributes: {
name: '',
description: '',
prefix: '',
icon: '',
required: false,
on_bio: false,
type: null,
validation: '',
},
});
m.redraw();
}

readyToAdd(field) {
return !!field.name();
}

availableTypes() {
return {
url: app.translator.trans('fof-masquerade.admin.types.url'),
email: app.translator.trans('fof-masquerade.admin.types.email'),
boolean: app.translator.trans('fof-masquerade.admin.types.boolean'),
select: app.translator.trans('fof-masquerade.admin.types.select'),
null: app.translator.trans('fof-masquerade.admin.types.advanced'),
};
}
}
16 changes: 16 additions & 0 deletions js/src/admin/components/FieldList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Button from 'flarum/common/components/Button';
import FieldEdit from './FieldEdit';

export default class FieldList {
view(vnode) {
const { existing, new: newField, loading, onUpdate } = vnode.attrs;

return m(
'form.js-sortable-fields',
existing.map((field) => {
return m(FieldEdit, { field, loading, onUpdate });
}),
m(FieldEdit, { field: newField, loading, onUpdate })
);
}
}
Loading

0 comments on commit 66dafe5

Please sign in to comment.