mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2024-11-29 10:24:20 +01:00
(ui) Update machines layout
This commit is contained in:
parent
5fd2d3fc79
commit
49a06a9176
@ -117,8 +117,12 @@ export const MachineCategoriesList: React.FC<MachineCategoriesListProps> = ({ on
|
||||
|
||||
return (
|
||||
<div className="machine-categories-list">
|
||||
<h3 className="machines-categories">{t('app.admin.machine_categories_list.machine_categories')}</h3>
|
||||
<FabButton onClick={addMachineCategory} className="is-secondary" >{t('app.admin.machine_categories_list.add_a_machine_category')}</FabButton>
|
||||
<header>
|
||||
<h2>{t('app.admin.machine_categories_list.machine_categories')}</h2>
|
||||
<div className='grpBtn'>
|
||||
<FabButton className="main-action-btn" onClick={addMachineCategory}>{t('app.admin.machine_categories_list.add_a_machine_category')}</FabButton>
|
||||
</div>
|
||||
</header>
|
||||
<MachineCategoryModal isOpen={modalIsOpen}
|
||||
machines={machines}
|
||||
machineCategory={machineCategory}
|
||||
|
@ -18,6 +18,19 @@ export const MachinesFilters: React.FC<MachinesFiltersProps> = ({ onFilterChange
|
||||
const defaultValue = { value: true, label: t('app.public.machines_filters.status_enabled') };
|
||||
const categoryDefaultValue = { value: null, label: t('app.public.machines_filters.all_machines') };
|
||||
|
||||
// Styles the React-select component
|
||||
const customStyles = {
|
||||
control: base => ({
|
||||
...base,
|
||||
width: '20ch',
|
||||
border: 'none',
|
||||
backgroundColor: 'transparent'
|
||||
}),
|
||||
indicatorSeparator: () => ({
|
||||
display: 'none'
|
||||
})
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides boolean options in the react-select format (yes/no/all)
|
||||
*/
|
||||
@ -56,21 +69,23 @@ export const MachinesFilters: React.FC<MachinesFiltersProps> = ({ onFilterChange
|
||||
return (
|
||||
<div className="machines-filters">
|
||||
<div className="filter-item">
|
||||
<label htmlFor="status">{t('app.public.machines_filters.show_machines')}</label>
|
||||
<p>{t('app.public.machines_filters.show_machines')}</p>
|
||||
<Select defaultValue={defaultValue}
|
||||
id="status"
|
||||
className="status-select"
|
||||
onChange={handleStatusSelected}
|
||||
options={buildBooleanOptions()}/>
|
||||
options={buildBooleanOptions()}
|
||||
styles={customStyles}/>
|
||||
</div>
|
||||
{machineCategories.length > 0 &&
|
||||
<div className="filter-item">
|
||||
<label htmlFor="category">{t('app.public.machines_filters.filter_by_machine_category')}</label>
|
||||
<p>{t('app.public.machines_filters.filter_by_machine_category')}</p>
|
||||
<Select defaultValue={categoryDefaultValue}
|
||||
id="machine_category"
|
||||
className="category-select"
|
||||
onChange={handleCategorySelected}
|
||||
options={buildCategoriesOptions()}/>
|
||||
options={buildCategoriesOptions()}
|
||||
styles={customStyles}/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
@ -12,6 +12,8 @@ import { MachinesFilters } from './machines-filters';
|
||||
import { User } from '../../models/user';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
import { EditorialBlock } from '../base/editorial-block';
|
||||
import { CalendarBlank } from 'phosphor-react';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
@ -96,30 +98,14 @@ export const MachinesList: React.FC<MachinesListProps> = ({ onError, onSuccess,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Go to store
|
||||
*/
|
||||
const linkToStore = (): void => {
|
||||
window.location.href = '/#!/store';
|
||||
};
|
||||
|
||||
// TODO: Conditionally display the store ad
|
||||
return (
|
||||
<div className="machines-list">
|
||||
{/* TODO: Condition to display editorial block */}
|
||||
{false &&
|
||||
<EditorialBlock />
|
||||
}
|
||||
<MachinesFilters onFilterChangedBy={handleFilterChangedBy} machineCategories={machineCategories}/>
|
||||
<div className="all-machines">
|
||||
{false &&
|
||||
<div className='store-ad' onClick={() => linkToStore}>
|
||||
<div className='content'>
|
||||
<h3>{t('app.public.machines_list.store_ad.title')}</h3>
|
||||
<p>{t('app.public.machines_list.store_ad.buy')}</p>
|
||||
<p className='sell'>{t('app.public.machines_list.store_ad.sell')}</p>
|
||||
</div>
|
||||
<FabButton icon={<i className="fa fa-cart-plus fa-lg" />} className="cta" onClick={linkToStore}>
|
||||
{t('app.public.machines_list.store_ad.link')}
|
||||
</FabButton>
|
||||
</div>
|
||||
}
|
||||
{machines && machines.map(machine => {
|
||||
return <MachineCard key={machine.id}
|
||||
user={user}
|
||||
|
@ -0,0 +1,153 @@
|
||||
import * as React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { IApplication } from '../../models/application';
|
||||
import { Loader } from '../base/loader';
|
||||
import { react2angular } from 'react2angular';
|
||||
import { ErrorBoundary } from '../base/error-boundary';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm, SubmitHandler } from 'react-hook-form';
|
||||
import { FormRichText } from '../form/form-rich-text';
|
||||
import { FormSwitch } from '../form/form-switch';
|
||||
import { FormInput } from '../form/form-input';
|
||||
import { FabButton } from '../base/fab-button';
|
||||
|
||||
declare const Application: IApplication;
|
||||
|
||||
interface MachinesSettingsProps {
|
||||
onError: (message: string) => void,
|
||||
onSuccess: (message: string) => void,
|
||||
}
|
||||
|
||||
/**
|
||||
* Machines settings
|
||||
*/
|
||||
export const MachinesSettings: React.FC<MachinesSettingsProps> = () => {
|
||||
const { t } = useTranslation('admin');
|
||||
const { register, control, formState, handleSubmit } = useForm();
|
||||
|
||||
// regular expression to validate the input fields
|
||||
const urlRegex = /^(https?:\/\/)([^.]+)\.(.{2,30})(\/.*)*\/?$/;
|
||||
|
||||
const [isActiveAutoCancellation, setIsActiveAutoCancellation] = useState<boolean>(false);
|
||||
const [isActiveAuthorizationValidity, setIsActiveAuthorizationValidity] = useState<boolean>(false);
|
||||
const [isActiveTextBlock, setIsActiveTextBlock] = useState<boolean>(false);
|
||||
const [isActiveValidationRule, setIsActiveValidationRule] = useState<boolean>(false);
|
||||
const [isActiveCta, setIsActiveCta] = useState<boolean>(false);
|
||||
|
||||
/**
|
||||
* Callback triggered when the auto cancellation switch has changed.
|
||||
*/
|
||||
const toggleAutoCancellation = (value: boolean) => {
|
||||
setIsActiveAutoCancellation(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the authorisation validity switch has changed.
|
||||
*/
|
||||
const toggleAuthorizationValidity = (value: boolean) => {
|
||||
setIsActiveAuthorizationValidity(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the authorisation validity switch has changed.
|
||||
*/
|
||||
const toggleValidationRule = (value: boolean) => {
|
||||
setIsActiveValidationRule(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the text block switch has changed.
|
||||
*/
|
||||
const toggleTextBlockSwitch = (value: boolean) => {
|
||||
setIsActiveTextBlock(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the CTA switch has changed.
|
||||
*/
|
||||
const toggleTextBlockCta = (value: boolean) => {
|
||||
setIsActiveCta(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the CTA label has changed.
|
||||
*/
|
||||
const handleCtaLabelChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
console.log('cta label:', event.target.value);
|
||||
};
|
||||
/**
|
||||
* Callback triggered when the cta url has changed.
|
||||
*/
|
||||
const handleCtaUrlChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
console.log('cta url:', event.target.value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback triggered when the form is submitted: save the settings
|
||||
*/
|
||||
const onSubmit: SubmitHandler<any> = (data) => {
|
||||
console.log(data);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="machines-settings">
|
||||
<header>
|
||||
<h2>{t('app.admin.machines_settings.title')}</h2>
|
||||
<FabButton onClick={handleSubmit(onSubmit)} className='save-btn is-main'>{t('app.admin.machines_settings.save')}</FabButton>
|
||||
</header>
|
||||
<form className="machines-settings-content">
|
||||
<div className="settings-section">
|
||||
<header>
|
||||
<p className="title">{t('app.admin.machines_settings.generic_text_block')}</p>
|
||||
<p className="description">{t('app.admin.machines_settings.generic_text_block_info')}</p>
|
||||
</header>
|
||||
|
||||
<div className="content">
|
||||
<FormSwitch id="active_text_block" control={control}
|
||||
onChange={toggleTextBlockSwitch} formState={formState}
|
||||
defaultValue={isActiveTextBlock}
|
||||
label={t('app.admin.machines_settings.generic_text_block_switch')} />
|
||||
|
||||
<FormRichText id="text_block"
|
||||
control={control}
|
||||
heading
|
||||
limit={280}
|
||||
disabled={!isActiveTextBlock} />
|
||||
|
||||
{isActiveTextBlock && <>
|
||||
<FormSwitch id="active_cta" control={control}
|
||||
onChange={toggleTextBlockCta} formState={formState}
|
||||
label={t('app.admin.machines_settings.cta_switch')} />
|
||||
|
||||
{isActiveCta && <>
|
||||
<FormInput id="cta_label"
|
||||
register={register}
|
||||
rules={{ required: true }}
|
||||
onChange={handleCtaLabelChange}
|
||||
maxLength={40}
|
||||
label={t('app.admin.machines_settings.cta_label')} />
|
||||
<FormInput id="cta_url"
|
||||
register={register}
|
||||
rules={{ required: true, pattern: urlRegex }}
|
||||
onChange={handleCtaUrlChange}
|
||||
label={t('app.admin.machines_settings.cta_url')} />
|
||||
</>}
|
||||
</>}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const MachinesSettingsWrapper: React.FC<MachinesSettingsProps> = (props) => {
|
||||
return (
|
||||
<Loader>
|
||||
<ErrorBoundary>
|
||||
<MachinesSettings {...props} />
|
||||
</ErrorBoundary>
|
||||
</Loader>
|
||||
);
|
||||
};
|
||||
|
||||
Application.Components.component('machinesSettings', react2angular(MachinesSettingsWrapper, ['onError', 'onSuccess']));
|
@ -62,11 +62,12 @@
|
||||
@import "modules/invoices/vat-settings-modal";
|
||||
@import "modules/layout/header-page";
|
||||
@import "modules/machines/machine-card";
|
||||
@import "modules/machines/machine-categories";
|
||||
@import "modules/machines/machine-form";
|
||||
@import "modules/machines/machines-filters";
|
||||
@import "modules/machines/machines-list";
|
||||
@import "modules/machines/machines-settings";
|
||||
@import "modules/machines/required-training-modal";
|
||||
@import "modules/machines/machine-categories";
|
||||
@import "modules/payment-schedule/payment-schedule-dashboard";
|
||||
@import "modules/payment-schedule/payment-schedule-summary";
|
||||
@import "modules/payment-schedule/payment-schedules-list";
|
||||
|
@ -1,4 +1,15 @@
|
||||
.machine-categories-list {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header {
|
||||
padding-bottom: 0;
|
||||
@include header;
|
||||
gap: 2.4rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
@ -1,36 +1,40 @@
|
||||
.machines-filters {
|
||||
margin: 1.5em 0;
|
||||
max-width: 1600px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 0.8rem 2.4rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
background-color: var(--gray-soft);
|
||||
border-radius: var(--border-radius);
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
justify-content: flex-end;
|
||||
& > *:not(:first-child) {
|
||||
&::before {
|
||||
content: "";
|
||||
margin: 0 2rem;
|
||||
width: 1px;
|
||||
height: 2rem;
|
||||
background-color: var(--gray-hard-darkest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
&:first-child {
|
||||
padding-right: 20px;
|
||||
}
|
||||
& {
|
||||
display: block;
|
||||
width: 50%;
|
||||
}
|
||||
& > label {
|
||||
white-space: nowrap;
|
||||
line-height: 2em;
|
||||
}
|
||||
& > * {
|
||||
display: inline-block;
|
||||
}
|
||||
.status-select, .category-select {
|
||||
width: 100%;
|
||||
}
|
||||
display: flex;
|
||||
align-items: center;
|
||||
p { margin: 0 0.8rem 0 0; }
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 720px){
|
||||
.machines-filters {
|
||||
display: block;
|
||||
.filter-item {
|
||||
padding-right: 0 !important;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
//@media screen and (max-width: 720px){
|
||||
// .machines-filters {
|
||||
// display: block;
|
||||
// .filter-item {
|
||||
// padding-right: 0 !important;
|
||||
// display: block;
|
||||
// width: 100%;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
@ -1,6 +1,24 @@
|
||||
.machines-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.6rem;
|
||||
|
||||
&-container {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header {
|
||||
padding-bottom: 0;
|
||||
@include header;
|
||||
gap: 2.4rem;
|
||||
}
|
||||
}
|
||||
|
||||
.all-machines {
|
||||
max-width: 1600px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
||||
|
@ -0,0 +1,21 @@
|
||||
.machines-settings {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding-bottom: 6rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
& > header {
|
||||
padding-bottom: 0;
|
||||
@include header($sticky: true);
|
||||
gap: 2.4rem;
|
||||
}
|
||||
|
||||
&-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3.2rem;
|
||||
|
||||
.settings-section { @include layout-settings; }
|
||||
.save-btn { align-self: flex-start; }
|
||||
}
|
||||
}
|
@ -6,47 +6,44 @@
|
||||
<div class="center">
|
||||
<h1 translate>{{ 'app.admin.machines.the_fablab_s_machines' }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="grpBtn wrapper">
|
||||
<a ng-if="isAuthorized('admin')"
|
||||
role="button"
|
||||
class="btn btn-lg btn-warning bg-white b-2x rounded m-t-xs"
|
||||
ui-sref="app.admin.machines_new"
|
||||
title="{{'app.public.machines_list.add_a_machine' | translate}}">
|
||||
<i class="fas fa-plus"></i>
|
||||
</a>
|
||||
<a ng-if="isAuthorized(['admin', 'manager'])"
|
||||
role="button"
|
||||
ui-sref="app.admin.calendar"
|
||||
class="btn btn-lg btn-default rounded b-2x m-t-xs"
|
||||
title="{{'app.public.machines_list.new_availability' | translate}}">
|
||||
<i class="fa fa-calendar-check-o" aria-hidden="true"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="m-lg admin-machines-manage">
|
||||
<div class="row">
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
|
||||
<div>
|
||||
<uib-tabset justified="true" active="tabs.active">
|
||||
<!--<uib-tab heading="{{ 'app.admin.machines.machines_settings' | translate }}" index="1" select="selectTab()">
|
||||
<machines-settings on-error="onError" on-success="on-success"></machines-settings>
|
||||
</uib-tab>-->
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.machines.machines_settings' | translate }}" index="1" select="selectTab()">
|
||||
<machines-settings on-error="onError" on-success="on-success"></machines-settings>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.machines.all_machines' | translate }}" index="0" select="selectTab()">
|
||||
<div class="machines-list-container">
|
||||
<header>
|
||||
<h2 translate>{{ 'app.admin.machines.all_machines' }}</h2>
|
||||
<div class="grpBtn">
|
||||
<a ng-if="isAuthorized(['admin', 'manager'])"
|
||||
role="button"
|
||||
ui-sref="app.admin.calendar"
|
||||
class="fab-button"
|
||||
title="{{'app.public.machines_list.new_availability' | translate}}">
|
||||
<i class="far fa-calendar" aria-hidden="true"></i>
|
||||
</a>
|
||||
<a ng-if="isAuthorized('admin')"
|
||||
role="button"
|
||||
class="fab-button is-main"
|
||||
ui-sref="app.admin.machines_new"
|
||||
title="{{'app.public.machines_list.add_a_machine' | translate}}"
|
||||
translate>
|
||||
{{ 'app.admin.machines.add_a_machine' }}
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
<ng-include src="'/admin/machines/machines.html'"></ng-include>
|
||||
</div>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.machines.all_machines' | translate }}" index="0" select="selectTab()">
|
||||
<ng-include src="'/admin/machines/machines.html'"></ng-include>
|
||||
</uib-tab>
|
||||
<uib-tab heading="{{ 'app.admin.machines.manage_machines_categories' | translate }}" index="2" select="selectTab()">
|
||||
<ng-include src="'/admin/machines/categories.html'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab heading="{{ 'app.admin.machines.manage_machines_categories' | translate }}" index="2" select="selectTab()">
|
||||
<ng-include src="'/admin/machines/categories.html'"></ng-include>
|
||||
</uib-tab>
|
||||
|
||||
</uib-tabset>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</uib-tabset>
|
||||
</section>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<section class="m-lg"
|
||||
ui-tour="machines"
|
||||
<section ui-tour="machines"
|
||||
ui-tour-backdrop="true"
|
||||
ui-tour-template-url="'/shared/tour-step-template.html'"
|
||||
ui-tour-use-hotkeys="true"
|
||||
|
@ -4,8 +4,18 @@ en:
|
||||
machines:
|
||||
the_fablab_s_machines: "The FabLab's machines"
|
||||
all_machines: "All machines"
|
||||
add_a_machine: "Add a new machine"
|
||||
manage_machines_categories: "Manage machines categories"
|
||||
machines_settings: "Settings"
|
||||
machines_settings:
|
||||
title: "Settings"
|
||||
generic_text_block: "Editorial text block"
|
||||
generic_text_block_info: "Displays an editorial block above the list of machines visible to members."
|
||||
generic_text_block_switch: "Display editorial block"
|
||||
cta_switch: "Display a button"
|
||||
cta_label: "Button label"
|
||||
cta_url: "url"
|
||||
save: "Save"
|
||||
machine_categories_list:
|
||||
machine_categories: "Machines categories"
|
||||
add_a_machine_category: "Add a machine category"
|
||||
|
@ -229,11 +229,11 @@ en:
|
||||
sell: "If you also want to sell your creations, please let us know."
|
||||
link: "To the store"
|
||||
machines_filters:
|
||||
show_machines: "Show machines"
|
||||
show_machines: "Show machines:"
|
||||
status_enabled: "Enabled"
|
||||
status_disabled: "Disabled"
|
||||
status_all: "All"
|
||||
filter_by_machine_category: "Filter by category"
|
||||
filter_by_machine_category: "Filter by category:"
|
||||
all_machines: "All machines"
|
||||
machine_card:
|
||||
book: "Book"
|
||||
|
Loading…
Reference in New Issue
Block a user