Subida del módulo y tema de PrestaShop

This commit is contained in:
Kaloyan
2026-04-09 18:31:51 +02:00
parent 12c253296f
commit 16b3ff9424
39262 changed files with 7418797 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}

View File

@@ -0,0 +1,8 @@
{
"recommendations": [
"Vue.volar",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}

View File

@@ -0,0 +1,10 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
}

View File

@@ -0,0 +1,22 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
PuikAvatar: typeof import('@prestashopcorp/puik-components')['PuikAvatar']
PuikButton: typeof import('@prestashopcorp/puik-components')['PuikButton']
PuikCard: typeof import('@prestashopcorp/puik-components')['PuikCard']
PuikIcon: typeof import('@prestashopcorp/puik-components')['PuikIcon']
PuikLink: typeof import('@prestashopcorp/puik-components')['PuikLink']
PuikModal: typeof import('@prestashopcorp/puik-components')['PuikModal']
PuikTable: typeof import('@prestashopcorp/puik-components')['PuikTable']
PuikTag: typeof import('@prestashopcorp/puik-components')['PuikTag']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}

View File

@@ -0,0 +1,6 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
const component: DefineComponent<Record<string, unknown>, Record<string, unknown>, any>
export default component
}

View File

@@ -0,0 +1,22 @@
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
skipFormatting,
)

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Community: Wall of Fame</title>
</head>
<body>
<div id="wall-of-fame-vue-app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,7 @@
<script setup lang="ts">
import { RouterView } from 'vue-router';
</script>
<template>
<RouterView />
</template>

View File

@@ -0,0 +1 @@
@import "@prestashopcorp/puik-theme/index.css";

View File

@@ -0,0 +1,2 @@
@import './base.css';

View File

@@ -0,0 +1,11 @@
import "@/assets/main.css";
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
const app = createApp(App);
app.use(router);
app.mount("#wall-of-fame-vue-app");

View File

@@ -0,0 +1,15 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '@/views/HomeView.vue';
const router = createRouter({
history: createWebHashHistory(),
routes: [
{
path: '/',
name: 'home',
component: HomeView,
}
],
})
export default router

View File

@@ -0,0 +1,37 @@
export interface Company {
name: string
rank: number
merged_pull_requests: number
pull_requests_percent: number
avatar_url: string
html_url: string
}
export interface Contributor {
login: string
id: number
avatar_url: string
html_url: string
name: string
company: string | null
blog: string | null
location: string | null
bio: string | null
email_domain: string | null
contributions: number
repositories: Record<string, number>
categories: Record<string, {
total: number
repositories: Record<string, number>
}>
}
export interface NewContributor {
login: string
name: string
avatar_url: string
html_url: string
contributions: number
firstContributionAt: string
}

View File

@@ -0,0 +1,121 @@
<script setup lang="ts">
import 'vue3-carousel/carousel.css'
import { ref, onMounted } from 'vue'
import HeaderSectionView from '@/views/sections/HeaderSectionView.vue'
import TopSectionView from '@/views/sections/TopSectionView.vue'
import NewContributorsSectionView from '@/views/sections/NewContributorsSectionView.vue'
import ContributeSectionView from '@/views/sections/ContributeSectionView.vue'
import type { Company, Contributor, NewContributor } from '@/types'
const totalMergedPr = ref<number>(0)
const prestaMergedPrbyPercent = ref<number>(0)
const topCompanies = ref<Company[]>([])
const contributorsData = ref<Contributor[]>([])
const topContributors = ref<Contributor[]>([])
const newContributors = ref<NewContributor[]>([])
onMounted(async () => {
try {
const response = await fetch('https://contributors.prestashop-project.org/newcontributors.json')
if (!response.ok) throw new Error('Error loading new contributors')
const data: Record<string, NewContributor> = await response.json()
newContributors.value = Object.values(data)
} catch (error) {
console.error('Error loading new contributors:', error)
}
try {
const response = await fetch('https://contributors.prestashop-project.org/topcompanies.json')
if (!response.ok) throw new Error('Error loading top companies')
const data = await response.json()
topCompanies.value = data.companies.slice(0, 5)
const total: number =
data.companies.reduce((acc: number, company: Company) => acc + company.merged_pull_requests, 0)
+ data.community.merged_pull_requests
totalMergedPr.value = total ?? 0
const prestashopCompany = data.companies.find((company: Company) => company.name === 'PrestaShop')
prestaMergedPrbyPercent.value = prestashopCompany.pull_requests_percent ?? 0
} catch (error) {
console.error('Error loading top companies:', error)
}
try {
const response = await fetch('https://contributors.prestashop-project.org/contributors_prs.json')
if (!response.ok) throw new Error('Error loading contributors data')
const data = await response.json()
// Filter out non-contributor entries and nulls (e.g., "updatedAt") from the JSON object
const contributorsOnly = Object.values(data).filter(
(item): item is Contributor =>
item !== null && typeof item === 'object' && 'contributions' in item,
)
contributorsData.value = contributorsOnly
topContributors.value = contributorsOnly.slice(0, 5)
} catch (error) {
console.error('Error loading contributors data:', error)
}
})
</script>
<template>
<div class="wof-container">
<HeaderSectionView
:total-merged-pr="totalMergedPr"
:presta-merged-pr-by-percent="prestaMergedPrbyPercent"
/>
<main>
<TopSectionView :top-contributors="topContributors" :top-companies="topCompanies" />
<NewContributorsSectionView :new-contributors="newContributors" />
<ContributeSectionView
contributeLink="https://devdocs.prestashop-project.org/9/contribute/contribute-pull-requests/"
slackLink="https://www.prestashop-project.org/slack/"
/>
</main>
</div>
</template>
<style>
:root {
--wof-section-gap: 1.5rem;
--wof-section-padding: 2.5rem 1rem;
--wof-section-padding-lg: 4rem;
--wof-avatar-bg: #fff;
--wof-jumbotron-size-sm: 2.5rem;
--wof-h1-size-sm: 1.75rem;
}
.wof-section {
padding: var(--wof-section-padding);
display: flex;
flex-direction: column;
gap: var(--wof-section-gap);
}
@media (min-width: 768px) {
.wof-section {
padding: var(--wof-section-padding-lg);
}
}
.puik-tag p {
margin-bottom: 0;
}
a.puik-button:hover {
text-decoration: none;
}
.puik-avatar.puik-avatar--photo {
background-color: var(--wof-avatar-bg);
}
@media (max-width: 768px) {
.puik-brand-jumbotron {
font-size: var(--wof-jumbotron-size-sm);
}
.puik-h1 {
font-size: var(--wof-h1-size-sm);
}
}
</style>

View File

@@ -0,0 +1,92 @@
<script setup lang="ts">
import { type PuikTableHeader } from '@prestashopcorp/puik-components'
defineProps<{
title: string
description?: string
externalLinkHref?: string
externalLinkContent?: string
headers: PuikTableHeader[]
items: any[]
stickyLastCol?: boolean
fullWidth?: boolean
}>()
const emit = defineEmits<{
(e: 'view', item: any): void
(e: 'action', payload: any): void
}>()
</script>
<template>
<puik-card class="wof-top-card">
<div class="wof-top-card__title-container">
<h3 class="wof-top-card__title puik-h2">{{ title }}</h3>
<puik-button
v-if="externalLinkContent && externalLinkHref"
variant="secondary"
:aria-label="externalLinkContent"
class="wof-top-card__external-link"
>
<puik-link
:href="externalLinkHref"
target="_blank"
:aria-label="externalLinkContent"
>
{{ externalLinkContent }}
</puik-link>
</puik-button>
</div>
<p v-if="description" class="wof-top-card__description puik-body-default">{{ description }}</p>
<puik-table
v-if="items?.length"
:headers="headers"
:items="items"
:stickyLastCol="stickyLastCol"
:fullWidth="fullWidth"
>
<template
v-for="header in headers"
:key="header.value"
v-slot:[`item-${header.value}`]="slotProps"
>
<slot
:name="`item-${header.value}`"
v-bind="slotProps"
>
<span class="puik-body-default">{{ slotProps.item[header.value] }}</span>
</slot>
</template>
</puik-table>
</puik-card>
</template>
<style>
:root {
--wof-top-card-title-size-sm: 1.25rem;
}
.wof-top-card {
display: flex;
flex-direction: column;
flex-grow: 1;
max-width: 100%;
gap: 0 !important;
}
.wof-top-card__title-container {
display: flex;
align-items: center;
justify-content: space-between;
}
.wof-top-card__title,
.wof-top-card__description,
.wof-top-card__external-link {
margin-bottom: 1rem;
}
@media (max-width: 768px) {
.wof-top-card__title {
font-size: var(--wof-top-card-title-size-sm);
}
}
</style>

View File

@@ -0,0 +1,204 @@
<script setup lang="ts">
import type { Contributor } from '@/types'
const props = defineProps<{
contributor: Contributor
isOpen: boolean
}>()
const emit = defineEmits<{
(e: 'close'): void
}>()
const close = () => emit('close')
</script>
<template>
<puik-modal
class="wof-top-modal"
size="large"
variant="feedback"
:is-open="isOpen"
@close="close"
>
<puik-button class="wof-top-modal__close-btn" variant="text" size="sm" @click="close">
<puik-icon icon="close" font-size="1.25rem"/>
</puik-button>
<div class="wof-top-modal__container">
<div class="wof-top-modal__side-content">
<div class="wof-top-modal__avatar">
<img :src="contributor.avatar_url" alt="contributor avatar" />
</div>
<div class="wof-top-modal__title">
<h3 class="puik-h3">{{ contributor.name }}</h3>
<puik-tag v-if="contributor.company" :content="contributor.company" variant="blue" />
</div>
<div v-if="contributor.location" class="wof-top-modal__side-content__item">
<puik-icon icon="location_on" :fill="0" />
<div class="wof-top-modal__side-content__item-infos">
<span class="wof-top-modal__side-content__item-title puik-body-default">Location</span>
<span class="wof-top-modal__side-content__item-value puik-body-default">
{{ contributor.location }}
</span>
</div>
</div>
<div v-if="contributor.company" class="wof-top-modal__side-content__item">
<puik-icon icon="work" :fill="0" />
<div class="wof-top-modal__side-content__item-infos">
<span class="wof-top-modal__side-content__item-title puik-body-default">
Current role(s)
</span>
<span class="wof-top-modal__side-content__item-value puik-body-default">
{{ contributor.company}}
</span>
</div>
</div>
<div v-if="contributor.html_url" class="wof-top-modal__side-content__item">
<puik-icon icon="location_on" :fill="0" />
<div class="wof-top-modal__side-content__item-infos">
<span class="wof-top-modal__side-content__item-title puik-body-default">GitHub</span>
<puik-link
:href="contributor.html_url"
target="_blank"
aria-label="contributor github"
class="wof-top-modal__side-content__item-value puik-body-default"
>
{{ contributor.html_url }}
</puik-link>
</div>
</div>
<div v-if="contributor.blog" class="wof-top-modal__side-content__item">
<puik-icon icon="desktop_mac" :fill="0" />
<div class="wof-top-modal__side-content__item-infos">
<span class="wof-top-modal__side-content__item-title puik-body-default">Website</span>
<puik-link
:href="contributor.blog"
target="_blank"
aria-label="contributor blog"
class="wof-top-modal__side-content__item-value puik-body-default"
>
{{ contributor.blog }}
</puik-link>
</div>
</div>
</div>
<div class="wof-top-modal__main-content">
<p class="puik-body-default-medium">{{ contributor.contributions }} contributions</p>
<div class="wof-top-modal__categories">
<puik-card
class="wof-top-modal__categories__card"
v-for="(data, category) in contributor.categories"
:key="category"
>
<p class="puik-h2">{{ data.total }}</p>
<p class="puik-body-default">{{ category }}</p>
</puik-card>
</div>
</div>
</div>
</puik-modal>
</template>
<style>
:root {
--wof-color-bg-modal: #ffffff;
--wof-color-bg-modal-side-panel: #dddddd;
--wof-color-side-panel-item-value: #5e5e5e;
--wof-padding-top-modal: 8.5rem;
}
.wof-top-modal__close-btn {
position: absolute;
right: 1rem;
top: 1rem;
}
.wof-top-modal .puik-modal__dialogPanelContainer__dialogPanel {
background-color: var(--wof-color-bg-modal);
padding: 0;
}
.wof-top-modal .puik-modal__dialogPanelContainer {
padding-top: var(--wof-padding-top-modal);
}
.wof-top-modal__container {
display: flex;
flex-direction: column;
flex-grow: 1;
background-color: var(--wof-color-bg-modal-side-panel);
overflow: auto;
@media screen and (min-width: 768px) {
flex-direction: row;
}
}
.wof-top-modal__title h3 {
margin-bottom: 0;
}
.wof-top-modal__side-content {
padding: 20px;
min-width: min-content;
min-height: fit-content;
display: flex;
flex-direction: column;
align-items: self-start;
gap: 1rem;
background-color: var(--wof-color-bg-modal);
overflow-y: auto;
@media screen and (min-width: 768px) {
padding: 40px;
}
}
.wof-top-modal__avatar {
border-radius: 50%;
min-height: 128px;
overflow: hidden;
}
.wof-top-modal__avatar img {
width: 128px;
height: 128px;
object-fit: cover;
object-position: center;
border-radius: 50%;
}
.wof-top-modal__side-content__item {
display: flex;
align-items: start;
gap: 0.5rem;
}
.wof-top-modal__side-content__item-infos {
display: flex;
flex-direction: column;
}
.wof-top-modal__side-content__item-title {
line-height: 1;
}
.wof-top-modal__side-content__item-value {
color: var(--wof-color-side-panel-item-value);
}
.wof-top-modal__main-content {
padding: 20px;
flex-grow: 1;
display: flex;
flex-direction: column;
min-height: fit-content;
gap: 1rem;
overflow: auto;
@media screen and (min-width: 768px) {
padding: 40px;
}
}
.wof-top-modal__categories {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
.wof-top-modal__categories__card {
padding: 1rem;
max-height: fit-content;
display: flex;
flex-direction: column;
gap: 0;
}
.wof-top-modal__categories__card p {
margin: 0;
}
</style>

View File

@@ -0,0 +1,76 @@
<script setup lang="ts">
defineProps<{
contributeLink: string
slackLink: string
}>()
</script>
<template>
<section class="wof-section wof-contribute-section">
<div class="wof-contribute-section__content">
<h2 class="wof-contribute-section__tite puik-h2"> How to contribute?</h2>
<p class="wof-contribute-section__description puik-body-default">
Join the open-source movement by contributing to PrestaShop on GitHubwhether its code,
documentation, or ideas. Every contribution counts!
</p>
</div>
<div class="wof-contribute-section__links">
<a
:href="contributeLink"
target="_blank"
aria-label="Contribute to PrestaShop"
rel="noopener noreferrer"
>
<puik-button variant="primary">
Contribute
</puik-button>
</a>
<a
:href="slackLink"
target="_blank"
aria-label="join PrestaShop Slack Open Source"
rel="noopener noreferrer"
>
<puik-button variant="secondary">
Join Slack
</puik-button>
</a>
</div>
</section>
</template>
<style>
:root {
--wof-contribute-section-bg: #bde9c9;
--wof-contribute-section-link-hover-color: #ffffff;
--wof-description-max-width: 1200px;
}
.wof-contribute-section {
justify-content: center;
align-items: center;
background-color: var(--wof-contribute-section-bg);
}
.wof-contribute-section__content {
display: flex;
flex-direction: column;
align-items: center;
}
.wof-contribute-section__tite {
margin-bottom: 1rem;
}
.wof-contribute-section__description {
margin-bottom: 0;
max-width: var(--wof-description-max-width);
text-align: center;
}
.wof-contribute-section__links {
display: flex;
gap: 1rem;
}
.wof-contribute-section__links a.puik-button {
text-decoration: none !important;
}
.wof-contribute-section__links a.puik-button--primary:hover {
color: var(--wof-contribute-section-link-hover-color) !important;
}
</style>

View File

@@ -0,0 +1,82 @@
<script setup lang="ts">
defineProps<{
totalMergedPr: number
prestaMergedPrByPercent: number
}>()
</script>
<template>
<header class="wof-section wof-header-section">
<h1 class="wof-header-section__title puik-brand-jumbotron">MEET our community Heroes</h1>
<p class="wof-header-section__description puik-body-default">
From day one, PrestaShop has thrived as an open-source platform powered by a talented
community of developers, merchants, and contributors. We all work together to improve and
support the scalability of the PrestaShop e-commerce platform. By remaining the main
contributors to its development, PrestaShop ensures long-term sustainability for everyone in
the ecosystem. The project grows with each contribution, and with each contribution our
contributors expertise grows. Take a look at our community.
</p>
<div class="wof-header-section__kpis-container">
<div class="wof-header-section__kpis-item">
<span class="wof-header-section__kpis-value puik-brand-h1">{{ totalMergedPr }}</span>
<span class="wof-header-section__kpis-label puik-body-default">Total Contributions</span>
</div>
<div class="wof-header-section__kpis-item">
<span class="wof-header-section__kpis-value puik-brand-h1">
{{ prestaMergedPrByPercent }}%
</span>
<span class="wof-header-section__kpis-label puik-body-default">Contributions by PrestaShop</span>
</div>
<div class="wof-header-section__kpis-item">
<span class="wof-header-section__kpis-value puik-brand-h1">
{{ (100 - prestaMergedPrByPercent).toFixed(2) }}%
</span>
<span class="wof-header-section__kpis-label puik-body-default">Contributions by Community</span>
</div>
</div>
</header>
</template>
<style>
:root {
--wof-header-section-bg: #1d1d1b;
--wof-header-section-text: #ffffff;
--wof-description-max-width: 1200px;
}
.wof-header-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 2rem;
background-color: var(--wof-header-section-bg);
}
.wof-header-section * {
color: var(--wof-header-section-text);
}
.wof-header-section__title {
margin-bottom: 0;
text-align: center;
text-transform: uppercase;
}
.wof-header-section__description {
max-width: var(--wof-description-max-width);
text-align: center;
}
.wof-header-section__kpis-container {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 2rem;
}
.wof-header-section__kpis-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 190px;
}
</style>

View File

@@ -0,0 +1,159 @@
<script setup lang="ts">
import 'vue3-carousel/carousel.css'
import { Carousel, Slide, Navigation } from 'vue3-carousel'
import type { NewContributor } from '@/types'
defineProps<{
newContributors: NewContributor[]
}>()
const carousel_config = {
itemsToShow: 1,
gap: 16,
snapAlign: 'center' as const,
breakpointMode: 'carousel' as const,
breakpoints: {
0: {
itemsToShow: 1,
snapAlign: 'center' as const,
},
476: {
itemsToShow: 2,
snapAlign: 'start' as const,
},
992: {
itemsToShow: 3,
snapAlign: 'start' as const,
},
1024: {
itemsToShow: 4,
snapAlign: 'start' as const,
},
1200: {
itemsToShow: 5,
snapAlign: 'start' as const,
},
1600: {
itemsToShow: 6,
snapAlign: 'start' as const,
},
},
}
</script>
<template>
<section class="wof-section wof-new-contributors-section">
<div>
<h2 class="wof-new-contributors-section__title puik-h2">👋 Say hello to our new contributors</h2>
<p class="wof-new-contributors-section__description puik-body-default">
Fresh commits, fresh faces. Meet the contributors who just joined!
</p>
</div>
<Carousel v-bind="carousel_config">
<Slide v-for="(newContributor, index) in newContributors" :key="index">
<puik-card class="wof-new-contributors-section__card">
<img
class="wof-new-contributors-section__img"
:src="newContributor.avatar_url"
:alt="`${newContributor.name ?? newContributor.login} avatar`"
/>
<h3 class="puik-h3">{{ newContributor.name ?? newContributor.login}}</h3>
<p class="puik-body-default">{{ newContributor.login }}</p>
<p class="puik-body-small">{{ newContributor.contributions }} contribution{{ newContributor.contributions > 1 ? "s" : "" }}</p>
</puik-card>
</Slide>
<template #addons>
<div class="wof-carousel__nav-container">
<Navigation>
<template #prev>
<puik-icon icon="keyboard_arrow_left" />
</template>
<template #next>
<puik-icon icon="keyboard_arrow_right" />
</template>
</Navigation>
</div>
</template>
</Carousel>
</section>
</template>
<style>
:root {
--wof-new-contributors-section-bg: #a4dbe8;
--wof-carousel-nav-bg: #fff;
--wof-carousel-nav-border: #1d1d1b;
--wof-carousel-nav-disabled-bg: #f7f7f7;
--wof-carousel-nav-disabled-border: #ddd;
--wof-carousel-nav-hover: #000;
}
.wof-new-contributors-section {
background-color: var(--wof-new-contributors-section-bg);
}
.wof-new-contributors-section__card {
flex-grow: 1;
gap: 0;
}
.wof-new-contributors-section__card * {
margin-bottom: 0;
}
.wof-new-contributors-section__title {
margin-bottom: 1rem;
}
.wof-new-contributors-section__description {
margin-bottom: 0;
padding-right: 96px;
}
.wof-new-contributors-section__img {
width: 100%;
object-fit: cover;
object-position: center;
}
.carousel {
--vc-nav-border-radius: 50%;
--vc-nav-width: 36px;
--vc-nav-height: 36px;
}
.wof-carousel__nav-container {
margin: 1rem 0 1.5rem 1rem;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
right: 0;
bottom: 100%;
gap: 0.5rem;
}
.wof-carousel__nav-container .carousel__next,
.wof-carousel__nav-container .carousel__prev {
background: var(--vc-nav-background, var(--wof-carousel-nav-bg));
background-color: var(--wof-carousel-nav-bg);
border: 1px solid var(--wof-carousel-nav-border);
border-radius: var(--vc-nav-border-radius);
color: var(--vc-nav-color);
font-size: var(--vc-nav-height);
height: var(--vc-nav-height);
position: relative;
transform: translateY(0);
width: var(--vc-nav-width);
}
.wof-carousel__nav-container .carousel__next--disabled,
.wof-carousel__nav-container .carousel__prev--disabled {
background-color: var(--wof-carousel-nav-disabled-bg);
border-color: var(--wof-carousel-nav-disabled-border);
opacity: 1;
}
.wof-carousel__nav-container .carousel__next--disabled .puik-icon,
.wof-carousel__nav-container .carousel__prev--disabled .puik-icon {
opacity: 0.3;
}
.wof-carousel__nav-container .carousel__next:hover,
.wof-carousel__nav-container .carousel__prev:hover {
color: var(--wof-carousel-nav-hover);
}
</style>

View File

@@ -0,0 +1,68 @@
<script setup lang="ts">
import TopContributorsView from '@/views/sections/sub-sections/TopContributorsView.vue';
import TopCompaniesView from '@/views/sections/sub-sections/TopCompaniesView.vue';
import type { Contributor, Company } from '@/types';
defineProps<{
topContributors: Contributor[]
topCompanies: Company[]
}>()
</script>
<template>
<section class="wof-section wof-top-section">
<h2 class="wof-top-section__title puik-h1">PrestaShop Projects top contributors</h2>
<div class="wof-top-section__cards">
<TopCompaniesView :top-companies="topCompanies" />
<TopContributorsView :top-contributors="topContributors" />
</div>
</section>
</template>
<style>
:root {
--wof-top-section-padding: 2.5rem 1rem;
--wof-top-section-padding-lg: 4rem;
--wof-top-section-rank-first: #ffd999;
--wof-top-section-rank-second: #eeeeee;
--wof-top-section-rank-third: #e7bd94;
}
.wof-section.wof-top-section {
padding: var(--wof-top-section-padding);
}
@media (min-width: 768px) {
.wof-section.wof-top-section {
padding: var(--wof-top-section-padding-lg);
}
}
.wof-top-section__title {
margin-bottom: 0;
}
.wof-top-section__cards {
display: flex;
flex-wrap: wrap;
gap: 2rem;
}
.wof-top-section__rank {
width: 1.5rem;
height: 1.5rem;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
}
.wof-top-section__rank span {
line-height: 0;
}
.wof-top-section__rank--first {
background-color: var(--wof-top-section-rank-first);
}
.wof-top-section__rank--second {
background-color: var(--wof-top-section-rank-second);
}
.wof-top-section__rank--third {
background-color: var(--wof-top-section-rank-third);
}
</style>

View File

@@ -0,0 +1,10 @@
<template>
<section class="wof-section wof-wall-of-fame-section">
<div>
<h2 class="puik-h2">🏆 PrestaShop Projects Wall of fame</h2>
<p class="puik-body-default">
The PrestaShop Wall of Fame: built by the best, committed to the core.
</p>
</div>
</section>
</template>

View File

@@ -0,0 +1,100 @@
<script setup lang="ts">
import { ref } from 'vue'
import TopCard from '@/views/components/TopCard.vue'
import type { PuikTableHeader } from '@prestashopcorp/puik-components'
import type { Company } from '@/types'
defineProps<{
topCompanies: Company[]
}>()
const headers: PuikTableHeader[] = [
{
text: 'Rank',
value: 'rank',
size: 'sm',
align: 'center',
searchable: false,
},
{
text: 'Logo',
value: 'logo',
size: 'sm',
align: 'center',
searchable: false,
},
{
text: 'Name',
value: 'name',
size: 'md',
align: 'left',
searchable: true,
},
{
text: 'Contributions',
value: 'merged_pull_requests',
size: 'sm',
align: 'center',
searchable: false,
},
{
value: 'actions',
size: 'sm',
align: 'center',
preventExpand: true,
searchSubmit: true,
},
]
const stickyLastCol = ref(false)
const fullWidth = ref(true)
</script>
<template>
<TopCard
title="🚀 Top companies"
description="Meet the top companies who are helping us strengthen PrestaShop."
:headers="headers"
:items="topCompanies"
:stickyLastCol="stickyLastCol"
:full-width="fullWidth"
>
<template #item-rank="{ index }">
<div
:class="[
'wof-top-section__rank',
{ 'wof-top-section__rank--first': index === 0 },
{ 'wof-top-section__rank--second': index === 1 },
{ 'wof-top-section__rank--third': index === 2 }
]"
>
<span class="puik-body-default-bold">{{ index + 1 }}</span>
</div>
</template>
<template #item-logo="{ item }">
<puik-avatar v-if="item.avatar_url" size="large" type="photo" :src="item.avatar_url" />
<puik-avatar v-else size="large" :first-name="item.name" :single-initial="false" />
</template>
<template #item-name="{ item }">
<div class="wof-top-contributors__name">
<span class="puik-body-default">{{ item.name }}</span>
</div>
</template>
<template #item-actions="{ item }">
<a
:href="item.html_url"
target="_blank"
aria-label="view profile"
rel="noopener noreferrer"
>
<puik-button
variant="text"
right-icon="visibility"
aria-label="view profile icon"
/>
</a>
</template>
</TopCard>
</template>

View File

@@ -0,0 +1,115 @@
<script setup lang="ts">
import { ref } from 'vue'
import TopCard from '@/views/components/TopCard.vue'
import TopModal from '@/views/components/TopModal.vue'
import type { PuikTableHeader } from '@prestashopcorp/puik-components'
import type { Contributor } from '@/types'
defineProps<{
topContributors: Contributor[]
}>()
const headers: PuikTableHeader[] = [
{
text: 'Rank',
value: 'rank',
size: 'sm',
align: 'center',
searchable: false,
},
{
text: 'Avatar',
value: 'avatar',
size: 'sm',
align: 'center',
searchable: false,
},
{
text: 'Name',
value: 'name',
size: 'md',
align: 'left',
searchable: true,
},
{
text: 'Contributions',
value: 'mergedPullRequests',
size: 'sm',
align: 'center',
searchable: false,
},
{
value: 'actions',
size: 'sm',
align: 'center',
preventExpand: true,
searchSubmit: true,
},
]
const stickyLastCol = ref(false)
const fullWidth = ref(true)
const modalContributorItem = ref()
const isModalOpen = ref(false)
const openModal = (contributor: any) => {
modalContributorItem.value = contributor
isModalOpen.value = true
}
const closeModal = () => {
isModalOpen.value = false
}
</script>
<template>
<TopCard
title="🔥 Top contributors"
description="These experts spent hours improving PrestaShop's quality."
external-link-content="View all"
external-link-href="https://contributors.prestashop-project.org/"
:headers="headers"
:items="topContributors"
:stickyLastCol="stickyLastCol"
:full-width="fullWidth"
@view="openModal"
>
<template #item-rank="{ index }">
<div
:class="[
'wof-top-section__rank',
{ 'wof-top-section__rank--first': index === 0 },
{ 'wof-top-section__rank--second': index === 1 },
{ 'wof-top-section__rank--third': index === 2 }
]"
>
<span class="puik-body-default-bold">{{ index + 1 }}</span>
</div>
</template>
<template #item-avatar="{ item }">
<puik-avatar size="large" type="photo" :src="item.avatar_url" />
</template>
<template #item-name="{ item }">
<div class="wof-top-contributors__name">
<span v-if="item.name" class="puik-body-default">{{ item.name }}</span>
<puik-tag v-if="item.company" :content="item.company" variant="blue" />
</div>
</template>
<template #item-actions="{ item }">
<puik-button
@click="openModal(item)"
variant="text"
right-icon="visibility"
aria-label="view profile"
/>
</template>
</TopCard>
<TopModal
v-if="modalContributorItem"
:contributor="modalContributorItem"
:isOpen="isModalOpen"
@close="closeModal"
/>
</template>

View File

@@ -0,0 +1,12 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"paths": {
"@/*": ["./src/*"]
}
}
}

View File

@@ -0,0 +1,11 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.node.json"
},
{
"path": "./tsconfig.app.json"
}
]
}

View File

@@ -0,0 +1,19 @@
{
"extends": "@tsconfig/node22/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"eslint.config.*"
],
"compilerOptions": {
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}

View File

@@ -0,0 +1,44 @@
import { fileURLToPath, URL } from 'node:url';
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import vueDevTools from 'vite-plugin-vue-devtools';
import Components from 'unplugin-vue-components/vite';
import AutoImport from 'unplugin-auto-import/vite';
import { PuikResolver } from '@prestashopcorp/puik-resolver';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
Components({
resolvers: [PuikResolver()],
}),
AutoImport({
resolvers: [PuikResolver()],
}),
cssInjectedByJsPlugin(),
],
server: {
origin: "http://localhost:5173",
},
base: '/modules/ps_distributionapiclient/views/js/vue/',
build: {
cssCodeSplit: false,
outDir: "../views/js/vue",
emptyOutDir: true,
rollupOptions: {
output: {
manualChunks: () => "index.js",
entryFileNames: `assets/[name].js`,
},
},
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})