discourse/app/assets/javascripts/admin/addon/components/themes-grid.gjs
Jordan Vidrine 3ad2fd032b
FEATURE: Initial themes config area with grid (#28828)
* UX: More additions

* UX: more

* DEV: Add admin/config/themes route

* UX: Use admin config card

* syntax merge fixes

* cleanup

* cleanup

* checkbox

* more

* error

* save on click

* more

* fix setter

* DEV: Implement vanilla checkbox

* cleanup

* UX: save themes as default

* DEV: Add component list to card

* DEV: Add placeholder for no screenshots

* DEV: Fix default theme reactivity

Also add content/optionalAction yields to config area
card and put the theme user selectable checkbox there,
along with adding styles.

* DEV: Change to generic "look and feel" config area

* DEV: Auto redirect to themes on base look and feel route

* UX: Remove computed from sorted themes

* linting

* UX: Turn update icon into button that routes to settings

* DEV: remove unused function

* UX: center icons with title

* DEV: Lint

* UX: Hook up theme preview button

* DEV: Minor fixes

---------

Co-authored-by: Martin Brennan <martin@discourse.org>
Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
2024-10-15 10:54:38 -05:00

117 lines
3.5 KiB
Plaintext

import Component from "@glimmer/component";
import { action } from "@ember/object";
import { service } from "@ember/service";
import DButton from "discourse/components/d-button";
import icon from "discourse-common/helpers/d-icon";
import i18n from "discourse-common/helpers/i18n";
import AdminConfigAreaCard from "admin/components/admin-config-area-card";
import InstallThemeModal from "../components/modal/install-theme";
import ThemesGridCard from "./themes-grid-card";
// NOTE (martin): Much of the JS code in this component is placeholder code. Much
// of the existing theme logic in /admin/customize/themes has old patterns
// and technical debt, so anything copied from there to here is subject
// to change as we improve this incrementally.
export default class ThemesGrid extends Component {
@service modal;
@service router;
externalResources = [
{
key: "admin.customize.theme.beginners_guide_title",
link: "https://meta.discourse.org/t/91966",
},
{
key: "admin.customize.theme.developers_guide_title",
link: "https://meta.discourse.org/t/93648",
},
{
key: "admin.customize.theme.browse_themes",
link: "https://meta.discourse.org/c/theme",
},
];
get sortedThemes() {
// Always show currently set default theme first
return this.args.themes.sort((a, b) => {
if (a.default) {
return -1;
} else if (b.default) {
return 1;
}
});
}
// TODO (martin) These install methods may not belong here and they
// are incomplete or have stubbed or omitted properties. We may want
// to move this to the new config route or a dedicated component
// that sits in the route.
installThemeOptions() {
return {
selectedType: "theme",
userId: null,
content: null,
installedThemes: this.args.themes,
addTheme: this.addTheme,
updateSelectedType: () => {},
};
}
@action
addTheme(theme) {
this.refresh();
theme.setProperties({ recentlyInstalled: true });
this.router.transitionTo("adminCustomizeThemes.show", theme.get("id"), {
queryParams: {
repoName: null,
repoUrl: null,
},
});
}
@action
installModal() {
this.modal.show(InstallThemeModal, {
model: { ...this.installThemeOptions() },
});
}
<template>
<div class="themes-cards-container">
{{#each this.sortedThemes as |theme|}}
<ThemesGridCard @theme={{theme}} @allThemes={{@themes}} />
{{/each}}
<AdminConfigAreaCard class="theme-card">
<:content>
<h2 class="theme-card__title">{{i18n
"admin.config_areas.look_and_feel.themes.new_theme"
}}</h2>
<p class="theme-card__description">{{i18n
"admin.customize.theme.themes_intro_new"
}}</p>
<div class="external-resources">
{{#each this.externalResources as |resource|}}
<a
href={{resource.link}}
class="external-link"
rel="noopener noreferrer"
target="_blank"
>
{{i18n resource.key}}
{{icon "external-link-alt"}}
</a>
{{/each}}
</div>
<DButton
@action={{this.installModal}}
@icon="upload"
@label="admin.customize.install"
class="btn-primary theme-card__button"
/>
</:content>
</AdminConfigAreaCard>
</div>
</template>
}