Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend Refactor #86

Merged
merged 11 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading