dev #40
@@ -5,17 +5,19 @@ Disallow: /admin
|
|||||||
Disallow: /admin/
|
Disallow: /admin/
|
||||||
Disallow: /*/admin
|
Disallow: /*/admin
|
||||||
Disallow: /*/admin/
|
Disallow: /*/admin/
|
||||||
|
Disallow: /order
|
||||||
Disallow: /order/
|
Disallow: /order/
|
||||||
|
Disallow: /*/order
|
||||||
Disallow: /*/order/
|
Disallow: /*/order/
|
||||||
|
Disallow: /co
|
||||||
Disallow: /co/
|
Disallow: /co/
|
||||||
|
Disallow: /*/co
|
||||||
Disallow: /*/co/
|
Disallow: /*/co/
|
||||||
Disallow: /checkout
|
Disallow: /checkout
|
||||||
Disallow: /checkout/
|
Disallow: /checkout/
|
||||||
|
Disallow: /checkout/cad
|
||||||
Disallow: /*/checkout
|
Disallow: /*/checkout
|
||||||
Disallow: /*/checkout/
|
Disallow: /*/checkout/
|
||||||
Disallow: /shop
|
Disallow: /*/checkout/cad
|
||||||
Disallow: /shop/
|
|
||||||
Disallow: /*/shop
|
|
||||||
Disallow: /*/shop/
|
|
||||||
|
|
||||||
Sitemap: https://3d-fab.ch/sitemap.xml
|
Sitemap: https://3d-fab.ch/sitemap.xml
|
||||||
|
|||||||
@@ -73,6 +73,16 @@
|
|||||||
<changefreq>weekly</changefreq>
|
<changefreq>weekly</changefreq>
|
||||||
<priority>0.8</priority>
|
<priority>0.8</priority>
|
||||||
</url>
|
</url>
|
||||||
|
<url>
|
||||||
|
<loc>https://3d-fab.ch/it/shop</loc>
|
||||||
|
<xhtml:link rel="alternate" hreflang="it" href="https://3d-fab.ch/it/shop" />
|
||||||
|
<xhtml:link rel="alternate" hreflang="en" href="https://3d-fab.ch/en/shop" />
|
||||||
|
<xhtml:link rel="alternate" hreflang="de" href="https://3d-fab.ch/de/shop" />
|
||||||
|
<xhtml:link rel="alternate" hreflang="fr" href="https://3d-fab.ch/fr/shop" />
|
||||||
|
<xhtml:link rel="alternate" hreflang="x-default" href="https://3d-fab.ch/it/shop" />
|
||||||
|
<changefreq>weekly</changefreq>
|
||||||
|
<priority>0.8</priority>
|
||||||
|
</url>
|
||||||
<url>
|
<url>
|
||||||
<loc>https://3d-fab.ch/it/about</loc>
|
<loc>https://3d-fab.ch/it/about</loc>
|
||||||
<xhtml:link rel="alternate" hreflang="it" href="https://3d-fab.ch/it/about" />
|
<xhtml:link rel="alternate" hreflang="it" href="https://3d-fab.ch/it/about" />
|
||||||
|
|||||||
@@ -130,7 +130,7 @@
|
|||||||
<button
|
<button
|
||||||
*ngFor="let language of mediaLanguages"
|
*ngFor="let language of mediaLanguages"
|
||||||
type="button"
|
type="button"
|
||||||
class="language-toggle-btn ui-language-toolbar__button"
|
class="ui-language-toolbar__button image-language-button"
|
||||||
[class.active]="
|
[class.active]="
|
||||||
getFormState(section.usageKey).activeLanguage ===
|
getFormState(section.usageKey).activeLanguage ===
|
||||||
language
|
language
|
||||||
@@ -139,11 +139,30 @@
|
|||||||
isLanguageComplete(section.usageKey, language)
|
isLanguageComplete(section.usageKey, language)
|
||||||
"
|
"
|
||||||
[class.incomplete]="
|
[class.incomplete]="
|
||||||
!isLanguageComplete(section.usageKey, language)
|
isLanguageIncomplete(section.usageKey, language)
|
||||||
|
"
|
||||||
|
[class.empty]="
|
||||||
|
!isLanguageStarted(section.usageKey, language)
|
||||||
"
|
"
|
||||||
(click)="setActiveLanguage(section.usageKey, language)"
|
(click)="setActiveLanguage(section.usageKey, language)"
|
||||||
>
|
>
|
||||||
{{ mediaLanguageLabels[language] }}
|
<span class="image-language-button__label">
|
||||||
|
{{ mediaLanguageLabels[language] }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="image-language-button__state"
|
||||||
|
*ngIf="isLanguageComplete(section.usageKey, language)"
|
||||||
|
>
|
||||||
|
OK
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
class="image-language-button__state"
|
||||||
|
*ngIf="
|
||||||
|
isLanguageIncomplete(section.usageKey, language)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
...
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,6 +27,62 @@
|
|||||||
gap: var(--space-1);
|
gap: var(--space-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.image-language-button {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.35rem;
|
||||||
|
min-width: 3.15rem;
|
||||||
|
background: #ffffff;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.empty {
|
||||||
|
opacity: 0.76;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.complete {
|
||||||
|
border-color: #b8ddc2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.incomplete {
|
||||||
|
border-color: #e8c8c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.active {
|
||||||
|
background: #fff5b8;
|
||||||
|
border-color: var(--color-brand);
|
||||||
|
color: var(--color-text);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button__label {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button__state {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: rgba(0, 0, 0, 0.08);
|
||||||
|
font-size: 0.62rem;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.complete .image-language-button__state {
|
||||||
|
background: #dcefdc;
|
||||||
|
color: #25603b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-language-button.incomplete .image-language-button__state {
|
||||||
|
background: #f7ddd7;
|
||||||
|
color: #944329;
|
||||||
|
}
|
||||||
|
|
||||||
.form-field--wide {
|
.form-field--wide {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,12 +17,16 @@ type HomeSectionKey =
|
|||||||
| 'capability-prototyping'
|
| 'capability-prototyping'
|
||||||
| 'capability-custom-parts'
|
| 'capability-custom-parts'
|
||||||
| 'capability-small-series'
|
| 'capability-small-series'
|
||||||
| 'capability-cad';
|
| 'capability-cad'
|
||||||
|
| 'joe'
|
||||||
|
| 'matteo';
|
||||||
|
|
||||||
|
type HomeMediaUsageType = 'HOME_SECTION' | 'ABOUT_MEMBER';
|
||||||
|
|
||||||
interface HomeMediaSectionConfig {
|
interface HomeMediaSectionConfig {
|
||||||
usageType: 'HOME_SECTION';
|
usageType: HomeMediaUsageType;
|
||||||
usageKey: HomeSectionKey;
|
usageKey: HomeSectionKey;
|
||||||
groupId: 'galleries' | 'capabilities';
|
groupId: 'galleries' | 'capabilities' | 'about-members';
|
||||||
title: string;
|
title: string;
|
||||||
preferredVariantName: 'card' | 'hero';
|
preferredVariantName: 'card' | 'hero';
|
||||||
}
|
}
|
||||||
@@ -94,6 +98,10 @@ export class AdminHomeMediaComponent implements OnInit, OnDestroy {
|
|||||||
id: 'capabilities',
|
id: 'capabilities',
|
||||||
title: 'Cosa puoi ottenere',
|
title: 'Cosa puoi ottenere',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'about-members',
|
||||||
|
title: 'Chi siamo',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
readonly sectionConfigs: readonly HomeMediaSectionConfig[] = [
|
readonly sectionConfigs: readonly HomeMediaSectionConfig[] = [
|
||||||
@@ -139,6 +147,20 @@ export class AdminHomeMediaComponent implements OnInit, OnDestroy {
|
|||||||
title: 'Home: consulenza e CAD',
|
title: 'Home: consulenza e CAD',
|
||||||
preferredVariantName: 'card',
|
preferredVariantName: 'card',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
usageType: 'ABOUT_MEMBER',
|
||||||
|
usageKey: 'joe',
|
||||||
|
groupId: 'about-members',
|
||||||
|
title: 'Chi siamo: Joe',
|
||||||
|
preferredVariantName: 'card',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
usageType: 'ABOUT_MEMBER',
|
||||||
|
usageKey: 'matteo',
|
||||||
|
groupId: 'about-members',
|
||||||
|
title: 'Chi siamo: Matteo',
|
||||||
|
preferredVariantName: 'card',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
sections: HomeMediaSectionView[] = [];
|
sections: HomeMediaSectionView[] = [];
|
||||||
@@ -155,6 +177,8 @@ export class AdminHomeMediaComponent implements OnInit, OnDestroy {
|
|||||||
'capability-custom-parts': this.createEmptyFormState(),
|
'capability-custom-parts': this.createEmptyFormState(),
|
||||||
'capability-small-series': this.createEmptyFormState(),
|
'capability-small-series': this.createEmptyFormState(),
|
||||||
'capability-cad': this.createEmptyFormState(),
|
'capability-cad': this.createEmptyFormState(),
|
||||||
|
joe: this.createEmptyFormState(),
|
||||||
|
matteo: this.createEmptyFormState(),
|
||||||
};
|
};
|
||||||
|
|
||||||
get configuredSectionCount(): number {
|
get configuredSectionCount(): number {
|
||||||
@@ -432,6 +456,25 @@ export class AdminHomeMediaComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isLanguageStarted(
|
||||||
|
sectionKey: HomeSectionKey,
|
||||||
|
language: AdminMediaLanguage,
|
||||||
|
): boolean {
|
||||||
|
return this.isTranslationStarted(
|
||||||
|
this.getFormState(sectionKey).translations[language],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLanguageIncomplete(
|
||||||
|
sectionKey: HomeSectionKey,
|
||||||
|
language: AdminMediaLanguage,
|
||||||
|
): boolean {
|
||||||
|
return (
|
||||||
|
this.isLanguageStarted(sectionKey, language) &&
|
||||||
|
!this.isLanguageComplete(sectionKey, language)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
getItemTranslation(
|
getItemTranslation(
|
||||||
item: HomeMediaItem,
|
item: HomeMediaItem,
|
||||||
language: AdminMediaLanguage,
|
language: AdminMediaLanguage,
|
||||||
@@ -619,6 +662,10 @@ export class AdminHomeMediaComponent implements OnInit, OnDestroy {
|
|||||||
return !!translation.title.trim() && !!translation.altText.trim();
|
return !!translation.title.trim() && !!translation.altText.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isTranslationStarted(translation: AdminMediaTranslation): boolean {
|
||||||
|
return !!translation.title.trim() || !!translation.altText.trim();
|
||||||
|
}
|
||||||
|
|
||||||
private validateTranslations(
|
private validateTranslations(
|
||||||
translations: Record<AdminMediaLanguage, AdminMediaTranslation>,
|
translations: Record<AdminMediaLanguage, AdminMediaTranslation>,
|
||||||
): string | null {
|
): string | null {
|
||||||
|
|||||||
@@ -8,25 +8,29 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="header-actions">
|
<div class="header-side ui-stack ui-stack--dense">
|
||||||
<article class="ui-stat-chip">
|
<div class="header-stats ui-inline-actions">
|
||||||
<strong>{{ products.length }}</strong>
|
<article class="ui-stat-chip">
|
||||||
<span>prodotti</span>
|
<strong>{{ products.length }}</strong>
|
||||||
</article>
|
<span>prodotti</span>
|
||||||
<article class="ui-stat-chip">
|
</article>
|
||||||
<strong>{{ categories.length }}</strong>
|
<article class="ui-stat-chip">
|
||||||
<span>categorie</span>
|
<strong>{{ categories.length }}</strong>
|
||||||
</article>
|
<span>categorie</span>
|
||||||
<button
|
</article>
|
||||||
type="button"
|
</div>
|
||||||
class="ui-button ui-button--ghost"
|
<div class="header-actions ui-inline-actions">
|
||||||
(click)="loadWorkspace()"
|
<button
|
||||||
>
|
type="button"
|
||||||
Aggiorna
|
class="ui-button ui-button--ghost"
|
||||||
</button>
|
(click)="loadWorkspace()"
|
||||||
<button type="button" class="ui-button" (click)="startCreateProduct()">
|
>
|
||||||
Nuovo prodotto
|
Aggiorna
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" class="ui-button" (click)="startCreateProduct()">
|
||||||
|
Nuovo prodotto
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
|||||||
@@ -80,6 +80,20 @@
|
|||||||
color: var(--color-text-muted);
|
color: var(--color-text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-side {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-stats {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: var(--space-2);
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
.header-actions {
|
.header-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -537,6 +551,15 @@ tbody tr.selected {
|
|||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-side {
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-stats,
|
||||||
|
.header-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.list-toolbar,
|
.list-toolbar,
|
||||||
.ui-form-grid--two {
|
.ui-form-grid--two {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
Reference in New Issue
Block a user