diff --git a/package-lock.json b/package-lock.json
index cc8775a..8b005dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -31,7 +31,7 @@
"@eslint/js": "^9.9.1",
"@iconify-json/flat-color-icons": "^1.1.11",
"@iconify-json/tabler": "^1.1.121",
- "@tailwindcss/typography": "^0.5.15",
+ "@tailwindcss/typography": "^0.5.16",
"@types/eslint__js": "^8.42.3",
"@types/js-yaml": "^4.0.9",
"@types/lodash.merge": "^4.6.9",
@@ -2195,10 +2195,11 @@
"integrity": "sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg=="
},
"node_modules/@tailwindcss/typography": {
- "version": "0.5.15",
- "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.15.tgz",
- "integrity": "sha512-AqhlCXl+8grUz8uqExv5OTtgpjuVIwFTSXTrh8y9/pw6q2ek7fJ+Y8ZEVw7EB2DCcuCOtEjf9w3+J3rzts01uA==",
+ "version": "0.5.16",
+ "resolved": "https://repo.huaweicloud.com/repository/npm/@tailwindcss/typography/-/typography-0.5.16.tgz",
+ "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"lodash.castarray": "^4.4.0",
"lodash.isplainobject": "^4.0.6",
@@ -2206,7 +2207,7 @@
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
- "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20"
+ "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@trysound/sax": {
diff --git a/package.json b/package.json
index 7c2992a..0ed579b 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,7 @@
"@eslint/js": "^9.9.1",
"@iconify-json/flat-color-icons": "^1.1.11",
"@iconify-json/tabler": "^1.1.121",
- "@tailwindcss/typography": "^0.5.15",
+ "@tailwindcss/typography": "^0.5.16",
"@types/eslint__js": "^8.42.3",
"@types/js-yaml": "^4.0.9",
"@types/lodash.merge": "^4.6.9",
diff --git a/src/assets/styles/tailwind.css b/src/assets/styles/tailwind.css
index 34be627..9395e09 100644
--- a/src/assets/styles/tailwind.css
+++ b/src/assets/styles/tailwind.css
@@ -1,6 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+@plugin '@tailwindcss/typography';
@font-face {
font-family: 'pinar';
diff --git a/src/components/widgets/MemberProse.astro b/src/components/widgets/MemberProse.astro
new file mode 100644
index 0000000..0ad1b4c
--- /dev/null
+++ b/src/components/widgets/MemberProse.astro
@@ -0,0 +1,16 @@
+---
+
+---
+
+
+
+
diff --git a/src/components/widgets/StaffCard.astro b/src/components/widgets/StaffCard.astro
index ec7bbae..b390be8 100644
--- a/src/components/widgets/StaffCard.astro
+++ b/src/components/widgets/StaffCard.astro
@@ -1,28 +1,34 @@
---
import { Icon } from 'astro-icon/components';
import { Image } from 'astro:assets';
-import type { Member } from '~/types';
import { getRelativeLocaleUrl } from 'astro:i18n';
+import type { CollectionEntry } from 'astro:content';
interface Props {
- staffMember: Member;
+ member: CollectionEntry<'member'>;
bg: string;
}
+function getMemberUrl(memberSlug: string) {
+ const [lang, ...slugParts] = memberSlug.split('/');
+ const baseSlug = slugParts.join('/');
+ return getRelativeLocaleUrl(lang, `members/${baseSlug}`);
+}
-const { staffMember, bg } = Astro.props;
+const { member, bg } = Astro.props as Props;
+const data = member.data;
---
- {staffMember.firstName}
+ {data.name}
+ {data.family}
- {staffMember.pfTitle}
+ {data.title}
- {staffMember.description}
+ {data.description}
-
-
-
+
+
+
diff --git a/src/components/widgets/StaffSection.astro b/src/components/widgets/StaffSection.astro
index 14a9ba1..8689e17 100644
--- a/src/components/widgets/StaffSection.astro
+++ b/src/components/widgets/StaffSection.astro
@@ -5,11 +5,15 @@ import background from '~/assets/images/members-background.jpg';
import SectionTitle from './SectionTitle.astro';
import StaffCard from './StaffCard.astro';
import i18n from '~/utils/i18n';
-import { staffMembers } from '~/data/mockStaffMembers';
-
-const translations = i18n.t();
+import { getCollection } from 'astro:content';
const { id, isDark = false, classes = {} } = Astro.props;
+const currentLocale = Astro.currentLocale!;
+const members = await getCollection('member');
+const effectiveMembers = members.filter(({ slug }) => slug.startsWith(currentLocale));
+
+i18n.setLang(currentLocale);
+const translations = i18n.t();
---
- {staffMembers.map((member) => )}
+ {effectiveMembers.map((member) => )}
diff --git a/src/content/member/en/abolfazlirani.md b/src/content/member/en/abolfazlirani.md
index ab0fd86..c6a0efa 100644
--- a/src/content/member/en/abolfazlirani.md
+++ b/src/content/member/en/abolfazlirani.md
@@ -2,9 +2,11 @@
name: 'Abolfazl'
family: 'Irani'
title: 'Programmer and Designer'
-image: '/images/members/amir.jpg'
+image: '/src/assets/images/avatar/sample-avatar-1.png' #'/images/members/amir.jpg'
description: 'Abolfazl is a mobile programmer who used to be a UI designer and has always tried to publish great apps.'
-topics: ['fa/open-source']
+topics:
+ - collection: 'topic'
+ slug: 'en/open-source'
social:
website: 'https://flutterfarsi.com'
github: 'https://github.com/abolfazlirani/'
diff --git a/src/content/member/en/payamzahedi95.md b/src/content/member/en/payamzahedi.md
similarity index 92%
rename from src/content/member/en/payamzahedi95.md
rename to src/content/member/en/payamzahedi.md
index 58d412c..af27924 100644
--- a/src/content/member/en/payamzahedi95.md
+++ b/src/content/member/en/payamzahedi.md
@@ -2,9 +2,11 @@
name: Payam
family: Zahedi
title: Software Engineer
-image: /images/members/amir.jpg
+image: '/src/assets/images/avatar/sample-avatar-4.png' #/images/members/amir.jpg
description: Payam is one of the co-founders of Flutter Persian and actively contributes to our open-source projects. He plays a key role in managing tasks and ensuring everything runs smoothly and efficiently! 🚀
-topics: ['en/open-source']
+topics:
+ - collection: 'topic'
+ slug: 'en/open-source'
social:
website: https://payamzahedi.com/
github: https://github.com/payam-zahedi/
diff --git a/src/content/member/fa/abolfazlirani.md b/src/content/member/fa/abolfazlirani.md
index ae0568a..629d10f 100644
--- a/src/content/member/fa/abolfazlirani.md
+++ b/src/content/member/fa/abolfazlirani.md
@@ -2,9 +2,11 @@
name: 'ابوالفضل'
family: 'ایرانی'
title: 'برنامهنویس و طراح'
-image: '/images/members/amir.jpg'
+image: '/src/assets/images/avatar/sample-avatar-1.png' #'/images/members/amir.jpg'
description: 'ابوالفضل یه برنامهنویس موبایله که در گذشتش طراح رابط کاربریم بوده و همیشه سعی کرده اپلیکیشنهای خوبی رو منتشر کنه.'
-topics: ['fa/open-source']
+topics:
+ - collection: 'topic'
+ slug: 'fa/open-source'
social:
website: 'https://flutterfarsi.com'
github: 'https://github.com/abolfazlirani/'
diff --git a/src/content/member/fa/payamzahedi95.md b/src/content/member/fa/payamzahedi.md
similarity index 94%
rename from src/content/member/fa/payamzahedi95.md
rename to src/content/member/fa/payamzahedi.md
index 4f097bc..9e266a5 100644
--- a/src/content/member/fa/payamzahedi95.md
+++ b/src/content/member/fa/payamzahedi.md
@@ -2,9 +2,11 @@
name: پیام
family: زاهدی
title: مهندس نرم افزار
-image: /images/members/amir.jpg
+image: '/src/assets/images/avatar/sample-avatar-4.png' #/images/members/amir.jpg
description: پیام، یکی از پایهگذارای فلاتر فارسیه و الان تو بخش پروژههای متنباز بهمون کمک میکنه و مدیریت کارا رو بر عهده داره. کلی حمایت میکنه که کارا بهتر و قویتر پیش برن! 🚀
-topics: ['fa/open-source']
+topics:
+ - collection: 'topic'
+ slug: 'fa/open-source'
social:
website: https://payamzahedi.com/
github: https://github.com/payam-zahedi/
diff --git a/src/data/mockStaffMembers.ts b/src/data/mockStaffMembers.ts
deleted file mode 100644
index 6d72819..0000000
--- a/src/data/mockStaffMembers.ts
+++ /dev/null
@@ -1,109 +0,0 @@
-import type { Member } from '~/types';
-import avatar1 from '~/assets/images/avatar/sample-avatar-1.png';
-import avatar2 from '~/assets/images/avatar/sample-avatar-2.png';
-import avatar3 from '~/assets/images/avatar/sample-avatar-3.png';
-import avatar4 from '~/assets/images/avatar/sample-avatar-4.png';
-import bigImage from '~/assets/images/bigImage.png';
-
-export const staffMembers: Member[] = [
- // lastNames, jobTitles, aboutMe, bigImage, skills, favorites, contact should be edited.
- // socials should be added.
- {
- slug: 'amir', // /members/[slug]
- firstName: 'امیر',
- lastName: 'نام خانوادگی',
- jobTitle: 'عنوان شغلی', //توسعه دهنده ارشد فلاتر مثلا
- pfTitle: 'توسعهدهنده ارشد و مشاور فنی',
- description: 'امیر با تخصص عمیق در فلاتر، راهنمای فنی پروژههای پیچیده و چالشبرانگیز است',
- aboutMe: 'امیر با تخصص عمیق در فلاتر، راهنمای فنی پروژههای پیچیده و چالشبرانگیز است', // توضیحات و درباره من که در صفحه شخصی قرار میگیرد
- image: { src: avatar1, alt: 'امیر' },
- bigImage: { src: bigImage, alt: 'امیر' }, // عکس بزرگ که در صفحه شخصی قرار میگیرد
- skills: [
- // بخش تخصص های من
- 'توسعه نرمافزارهای فلاتری و موبایل',
- 'طراحی سایتهای استاتیک با Astro',
- 'ساخت و طراحی اپلیکیشنهای Embedded',
- ' پروژههای DIY',
- ' مباحث مربوط به خانه هوشمند و Home Assistant',
- 'کار با مدلهای هوش مصنوعی و تجربه استفاده از آنها ',
- ],
- // بخش علاقه مندی ها
- favorites:
- 'به پروژهها و منابع Open Source علاقه زیادی دارم، به همین دلیل در بخش Open Source جامعه فلاتر فارسی فعال هستم. ',
- // بخش ارتباط با من
- contact:
- 'لینک شبکههای اجتماعی من را میتوانید توی این صفحه پیدا کنید. اگر سؤالی دارید یا فکر میکنید میتونم توی زمینه خاصی بهتون کمکی کنم، فقط کافیه هر جایی که راحتیت به من پیام بدید!',
- },
-
- {
- slug: 'sara',
- firstName: 'سارا',
- lastName: 'نام خانوادگی',
- jobTitle: 'عنوان شغلی',
- pfTitle: 'مسئول آموزش و منتورینگ',
- description: 'سارا با تجربه غنی در آموزش، مسیر یادگیری را برای اعضای جامعه هموار میکند',
- aboutMe: 'سارا با تجربه غنی در آموزش، مسیر یادگیری را برای اعضای جامعه هموار میکند',
- image: { src: avatar2, alt: 'سارا' },
- bigImage: { src: bigImage, alt: 'سارا' },
- skills: [
- 'توسعه نرمافزارهای فلاتری و موبایل',
- 'طراحی سایتهای استاتیک با Astro',
- 'ساخت و طراحی اپلیکیشنهای Embedded',
- ' پروژههای DIY',
- ' مباحث مربوط به خانه هوشمند و Home Assistant',
- 'کار با مدلهای هوش مصنوعی و تجربه استفاده از آنها ',
- ],
- favorites:
- 'به پروژهها و منابع Open Source علاقه زیادی دارم، به همین دلیل در بخش Open Source جامعه فلاتر فارسی فعال هستم. ',
- contact:
- 'لینک شبکههای اجتماعی من را میتوانید توی این صفحه پیدا کنید. اگر سؤالی دارید یا فکر میکنید میتونم توی زمینه خاصی بهتون کمکی کنم، فقط کافیه هر جایی که راحتیت به من پیام بدید!',
- },
-
- {
- slug: 'hadi',
- firstName: 'هادی',
- lastName: 'نام خانوادگی',
- jobTitle: 'عنوان شغلی',
- pfTitle: 'مدیر کافه فلاتر',
- description: 'هادی با برنامهریزی دقیق، کافه فلاتر را به یک رویداد منظم و ارزشمند تبدیل کرده است',
- aboutMe: 'هادی با برنامهریزی دقیق، کافه فلاتر را به یک رویداد منظم و ارزشمند تبدیل کرده است',
- image: { src: avatar3, alt: 'هادی' },
- bigImage: { src: bigImage, alt: 'هادی' },
- skills: [
- 'توسعه نرمافزارهای فلاتری و موبایل',
- 'طراحی سایتهای استاتیک با Astro',
- 'ساخت و طراحی اپلیکیشنهای Embedded',
- ' پروژههای DIY',
- ' مباحث مربوط به خانه هوشمند و Home Assistant',
- 'کار با مدلهای هوش مصنوعی و تجربه استفاده از آنها ',
- ],
- favorites:
- 'به پروژهها و منابع Open Source علاقه زیادی دارم، به همین دلیل در بخش Open Source جامعه فلاتر فارسی فعال هستم. ',
- contact:
- 'لینک شبکههای اجتماعی من را میتوانید توی این صفحه پیدا کنید. اگر سؤالی دارید یا فکر میکنید میتونم توی زمینه خاصی بهتون کمکی کنم، فقط کافیه هر جایی که راحتیت به من پیام بدید!',
- },
-
- {
- slug: 'parham',
- firstName: 'پرهام',
- lastName: 'نام خانوادگی',
- jobTitle: 'عنوان شغلی',
- pfTitle: 'مسئول رسانههای اجتماعی',
- description: 'پرهام با خلاقیت و نوآوری، حضور ما را در شبکههای اجتماعی مدیریت میکند',
- aboutMe: 'پرهام با خلاقیت و نوآوری، حضور ما را در شبکههای اجتماعی مدیریت میکند',
- image: { src: avatar4, alt: 'پرهام' },
- bigImage: { src: bigImage, alt: 'پرهام' },
- skills: [
- 'توسعه نرمافزارهای فلاتری و موبایل',
- 'طراحی سایتهای استاتیک با Astro',
- 'ساخت و طراحی اپلیکیشنهای Embedded',
- ' پروژههای DIY',
- ' مباحث مربوط به خانه هوشمند و Home Assistant',
- 'کار با مدلهای هوش مصنوعی و تجربه استفاده از آنها ',
- ],
- favorites:
- 'به پروژهها و منابع Open Source علاقه زیادی دارم، به همین دلیل در بخش Open Source جامعه فلاتر فارسی فعال هستم. ',
- contact:
- 'لینک شبکههای اجتماعی من را میتوانید توی این صفحه پیدا کنید. اگر سؤالی دارید یا فکر میکنید میتونم توی زمینه خاصی بهتون کمکی کنم، فقط کافیه هر جایی که راحتیت به من پیام بدید!',
- },
-];
diff --git a/src/pages/en/members/[slug].astro b/src/pages/en/members/[slug].astro
index 48844c5..0ef384d 100644
--- a/src/pages/en/members/[slug].astro
+++ b/src/pages/en/members/[slug].astro
@@ -1,33 +1,54 @@
---
import { Image } from 'astro:assets';
-import { staffMembers } from '~/data/mockStaffMembers';
-import Layout from '~/layouts/MembersLayout.astro';
+import MembersLayout from '~/layouts/MembersLayout.astro';
import i18n from '~/utils/i18n';
import bigImage from '~/assets/images/bigImage.png';
import { Icon } from 'astro-icon/components';
+import { getCollection } from 'astro:content';
+import MemberProse from '~/components/widgets/MemberProse.astro';
-const { slug } = Astro.params;
-const member = staffMembers.find((m) => m.slug === slug);
+const { member } = Astro.props;
+const data = member.data;
+const { Content } = await member.render();
-if (!member) {
- throw new Error('Member not found');
-}
+export async function getStaticPaths() {
+ const members = await getCollection('member');
+
+ return members
+ .filter(({ slug }) => slug.startsWith('en'))
+ .filter(({ slug }) => slug.split('/').length === 2)
+ .map((member) => {
+ const [, ...slugParts] = member.slug.split('/');
+ const baseSlug = slugParts.join('/');
-export function getStaticPaths() {
- return staffMembers.map((member) => ({
- params: { slug: member.slug },
- }));
+ return {
+ params: {
+ slug: baseSlug,
+ },
+ props: {
+ member,
+ },
+ };
+ });
}
+const socialIcons = {
+ website: 'tabler:world',
+ github: 'tabler:brand-github',
+ linkedin: 'tabler:brand-linkedin',
+ twitter: 'tabler:brand-x',
+ telegram: 'tabler:brand-telegram',
+};
+
const metadata = {
- title: `Persian Flutter | ${member.firstName}`,
+ title: `Persian Flutter | ${data.name}`,
ignoreTitleTemplate: true,
};
i18n.setLang(Astro.currentLocale);
---
-
+
- {`${member.firstName} ${member.lastName}`}
- {member.jobTitle}
- {member.description}
+ {`${data.name} ${data.family}`}
+ {data.title}
+ {data.description}
-
-
-
+ {
+ data.social &&
+ Object.entries(data.social).map(([key, link]) =>
+ link ? (
+
+
+
+ ) : null
+ )
+ }
-
-
{member.aboutMe}
-
-
تخصصهای من
-
{member.skills.map((skill) => - {skill}
)}
-
-
-
علاقهمندیها
-
{member.favorites}
-
-
-
ارتباط با من
-
{member.contact}
-
-
-
+
+
+
+
diff --git a/src/pages/en/members/index.astro b/src/pages/en/members/index.astro
index 0754245..5048ebf 100644
--- a/src/pages/en/members/index.astro
+++ b/src/pages/en/members/index.astro
@@ -1,8 +1,15 @@
---
import MembersLayout from '~/layouts/MembersLayout.astro';
import '~/assets/styles/custom.css';
-import { staffMembers } from '~/data/mockStaffMembers';
import StaffCard from '~/components/widgets/StaffCard.astro';
+import { getCollection } from 'astro:content';
+import i18n from '~/utils/i18n';
+
+const currentLocale = Astro.currentLocale!;
+const members = await getCollection('member');
+const effectiveMembers = members.filter(({ slug }) => slug.startsWith(currentLocale));
+
+i18n.setLang(currentLocale);
const metadata = {
title: 'Persian Flutter | Members',
@@ -14,6 +21,6 @@ const metadata = {
- {staffMembers.map((member) => )}
+ {effectiveMembers.map((member) => )}
diff --git a/src/pages/members/[slug].astro b/src/pages/members/[slug].astro
index 48844c5..db4ff86 100644
--- a/src/pages/members/[slug].astro
+++ b/src/pages/members/[slug].astro
@@ -1,33 +1,54 @@
---
import { Image } from 'astro:assets';
-import { staffMembers } from '~/data/mockStaffMembers';
-import Layout from '~/layouts/MembersLayout.astro';
+import MembersLayout from '~/layouts/MembersLayout.astro';
import i18n from '~/utils/i18n';
import bigImage from '~/assets/images/bigImage.png';
import { Icon } from 'astro-icon/components';
+import { getCollection } from 'astro:content';
+import MemberProse from '~/components/widgets/MemberProse.astro';
-const { slug } = Astro.params;
-const member = staffMembers.find((m) => m.slug === slug);
+const { member } = Astro.props;
+const data = member.data;
+const { Content } = await member.render();
-if (!member) {
- throw new Error('Member not found');
-}
+export async function getStaticPaths() {
+ const members = await getCollection('member');
+
+ return members
+ .filter(({ slug }) => slug.startsWith('fa'))
+ .filter(({ slug }) => slug.split('/').length === 2)
+ .map((member) => {
+ const [, ...slugParts] = member.slug.split('/');
+ const baseSlug = slugParts.join('/');
-export function getStaticPaths() {
- return staffMembers.map((member) => ({
- params: { slug: member.slug },
- }));
+ return {
+ params: {
+ slug: baseSlug,
+ },
+ props: {
+ member,
+ },
+ };
+ });
}
+const socialIcons = {
+ website: 'tabler:world',
+ github: 'tabler:brand-github',
+ linkedin: 'tabler:brand-linkedin',
+ twitter: 'tabler:brand-x',
+ telegram: 'tabler:brand-telegram',
+};
+
const metadata = {
- title: `Persian Flutter | ${member.firstName}`,
+ title: `Persian Flutter | ${data.name}`,
ignoreTitleTemplate: true,
};
i18n.setLang(Astro.currentLocale);
---
-
+
- {`${member.firstName} ${member.lastName}`}
- {member.jobTitle}
- {member.description}
+ {`${data.name} ${data.family}`}
+ {data.title}
+ {data.description}
-
-
-
+ {
+ data.social &&
+ Object.entries(data.social).map(([key, link]) =>
+ link ? (
+
+
+
+ ) : null
+ )
+ }
-
-
{member.aboutMe}
-
-
تخصصهای من
-
{member.skills.map((skill) => - {skill}
)}
-
-
-
علاقهمندیها
-
{member.favorites}
-
-
-
ارتباط با من
-
{member.contact}
-
-
-
+
+
+
+
diff --git a/src/pages/members/index.astro b/src/pages/members/index.astro
index 0754245..5048ebf 100644
--- a/src/pages/members/index.astro
+++ b/src/pages/members/index.astro
@@ -1,8 +1,15 @@
---
import MembersLayout from '~/layouts/MembersLayout.astro';
import '~/assets/styles/custom.css';
-import { staffMembers } from '~/data/mockStaffMembers';
import StaffCard from '~/components/widgets/StaffCard.astro';
+import { getCollection } from 'astro:content';
+import i18n from '~/utils/i18n';
+
+const currentLocale = Astro.currentLocale!;
+const members = await getCollection('member');
+const effectiveMembers = members.filter(({ slug }) => slug.startsWith(currentLocale));
+
+i18n.setLang(currentLocale);
const metadata = {
title: 'Persian Flutter | Members',
@@ -14,6 +21,6 @@ const metadata = {
- {staffMembers.map((member) => )}
+ {effectiveMembers.map((member) => )}
diff --git a/src/types.d.ts b/src/types.d.ts
index ed16aa3..f6c0257 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -60,22 +60,6 @@ export interface CafeFlutterItem {
googleCalendarLink?: string;
}
-export interface Member {
- slug: string;
- firstName: string;
- lastName: string;
- jobTitle: string;
- pfTitle: string;
- image: ImageMetadata;
- bigImage: ImageMetadata;
- socials?: Social[]; // after all socials will be added, the "?" should removed.
- description: string;
- aboutMe: string;
- skills: string[];
- favorites: string;
- contact: string;
-}
-
export interface Taxonomy {
slug: string;
title: string;