feat(incubators): add/ update data, data structure, types, and UI components#10
feat(incubators): add/ update data, data structure, types, and UI components#10TarekAeb wants to merge 1 commit intoalgeria-ecosystem:mainfrom
Conversation
✅ Deploy Preview for algeria-ecosystem ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
There was a problem hiding this comment.
Pull request overview
This PR significantly expands the incubators feature by adding comprehensive data from startup.dz, updating the data structure to include additional fields, and enhancing the UI to display this new information.
Key Changes:
- Expanded incubators dataset from ~10 entries to 87 entries with rich metadata including contact information, descriptions, and social media links
- Updated data structure to replace
foundedYearwithincubator_typeas the primary classification field, along with new optional fields for email, phone, address, and socials - Enhanced UI components to display new incubator information including type badges, descriptions, and multiple contact methods with appropriate icons
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/features/incubators/types.ts | Updated Incubator interface to reflect new data structure with incubator_type, contact fields, and made foundedYear optional |
| src/shared/components/SimpleFilterBar.tsx | Added type filter support and made sort functionality optional to accommodate different filtering needs |
| src/features/incubators/pages/Incubators.tsx | Removed year-based sorting, added type filtering, and updated search to include descriptions |
| src/features/incubators/components/IncubatorCard.tsx | Redesigned card layout to display new fields including type badge, description, social links, email, and phone with appropriate icons |
| src/data/incubators.json | Replaced limited dataset with 87 comprehensive incubator entries scraped from startup.dz |
| src/data/startups.json | Fixed trailing comma formatting issues |
| package-lock.json | Version bump to 0.2.0 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (url.includes('facebook')) return <Facebook className="w-4 h-4" />; | ||
| if (url.includes('linkedin')) return <Linkedin className="w-4 h-4" />; | ||
| if (url.includes('instagram')) return <Instagram className="w-4 h-4" />; | ||
| if (url.includes('twitter')) return <Twitter className="w-4 h-4" />; |
There was a problem hiding this comment.
The getSocialIcon function performs case-sensitive substring matching, which may fail if URLs contain uppercase variations (e.g., "Facebook.com" or "FACEBOOK"). Consider using toLowerCase() on the URL before checking to make the matching more robust.
| if (url.includes('facebook')) return <Facebook className="w-4 h-4" />; | |
| if (url.includes('linkedin')) return <Linkedin className="w-4 h-4" />; | |
| if (url.includes('instagram')) return <Instagram className="w-4 h-4" />; | |
| if (url.includes('twitter')) return <Twitter className="w-4 h-4" />; | |
| const normalizedUrl = url.toLowerCase(); | |
| if (normalizedUrl.includes('facebook')) return <Facebook className="w-4 h-4" />; | |
| if (normalizedUrl.includes('linkedin')) return <Linkedin className="w-4 h-4" />; | |
| if (normalizedUrl.includes('instagram')) return <Instagram className="w-4 h-4" />; | |
| if (normalizedUrl.includes('twitter')) return <Twitter className="w-4 h-4" />; |
src/features/incubators/types.ts
Outdated
| foundedYear: number; | ||
| website?: string; | ||
| linkedin?: string; | ||
| incubator_type?: string; |
There was a problem hiding this comment.
The property name 'incubator_type' uses snake_case naming convention, which is inconsistent with the JavaScript/TypeScript convention of camelCase used by other properties in this interface (e.g., 'foundedYear', 'mapLocation'). Consider renaming to 'incubatorType' for consistency.
| incubator_type?: string; | |
| incubatorType?: string; |
| {incubator.socials && incubator.socials.map((social, idx) => ( | ||
| <a | ||
| key={idx} |
There was a problem hiding this comment.
Using array index as key for map iteration is an anti-pattern when the items don't have unique identifiers. If the social links array is reordered or items are added/removed, React may incorrectly reuse components. Consider using the social URL itself as the key since it should be unique.
| {incubator.socials && incubator.socials.map((social, idx) => ( | |
| <a | |
| key={idx} | |
| {incubator.socials && incubator.socials.map((social) => ( | |
| <a | |
| key={social} |
| {incubator.socials && incubator.socials.map((social, idx) => ( | ||
| <a | ||
| key={idx} | ||
| href={social} | ||
| target="_blank" | ||
| rel="noopener noreferrer" | ||
| className="w-8 h-8 rounded-full flex items-center justify-center text-muted-foreground hover:text-primary hover:bg-primary/5 border border-transparent hover:border-primary/20 transition-all duration-300" | ||
| > | ||
| {getSocialIcon(social)} | ||
| </a> |
There was a problem hiding this comment.
Social media links are missing aria-label or accessible text for screen readers. Users relying on assistive technologies won't know which social platform each icon represents. Add an aria-label attribute that describes the platform (e.g., "Visit Facebook page", "Visit LinkedIn page").
| {incubator.email && ( | ||
| <a | ||
| href={`mailto:${incubator.email}`} | ||
| className="w-8 h-8 rounded-full flex items-center justify-center text-muted-foreground hover:text-primary hover:bg-primary/5 border border-transparent hover:border-primary/20 transition-all duration-300" | ||
| title={incubator.email} | ||
| > | ||
| <Mail className="w-4 h-4" /> | ||
| </a> | ||
| )} | ||
|
|
||
| {incubator.phone && ( | ||
| <a | ||
| href={`tel:${incubator.phone}`} | ||
| className="w-8 h-8 rounded-full flex items-center justify-center text-muted-foreground hover:text-primary hover:bg-primary/5 border border-transparent hover:border-primary/20 transition-all duration-300" | ||
| title={incubator.phone} | ||
| > | ||
| <Phone className="w-4 h-4" /> | ||
| </a> | ||
| )} |
There was a problem hiding this comment.
The email and phone links use the title attribute for accessibility, but title is not reliably announced by screen readers. Consider adding an aria-label attribute in addition to or instead of title to ensure the content is accessible to all users.
src/data/incubators.json
Outdated
| "phone": "0661123960", | ||
| "website": "https://manque de programme d acceleration" |
There was a problem hiding this comment.
The website URL appears to be invalid or contains a note in French rather than an actual website address. This should either be removed or replaced with a valid URL if one exists.
| "phone": "0661123960", | |
| "website": "https://manque de programme d acceleration" | |
| "phone": "0661123960" |
src/data/incubators.json
Outdated
| "address": "Université blida2", | ||
| "description": "حاضنة الأعمال لجامعة البليدة2 مهيكلة في شكل لجنة قيادية مشتركة تضم: ممثلين عن جامعة البليدة2، ممثلين من الوكالة لتثمين نتائج البحث العلمي واالتنمية التكنولوجية وممثل عن الشركاء الاقتصاديين. لأجل ترقية الفكر المقاولاتي في الوسط الجامعي وتعزيز روح المبادرة لدى الطلبة من أجل انشاء مشاريعهم، كما تدعم الإبداع والابتكار وتعمل على خلق تأثير حقيقي على الاقتصاد المحلي من خلال دعم الطلبة في طموحاته", | ||
| "email": "s.hadj_aissa@live.fr", | ||
| "website": "HTtPS://www.univ-blida2.dz" |
There was a problem hiding this comment.
URL protocol has inconsistent casing with mixed case letters in 'HTtPS'. This should be normalized to lowercase 'https' to ensure consistent URL formatting across the dataset.
| "website": "HTtPS://www.univ-blida2.dz" | |
| "website": "https://www.univ-blida2.dz" |
src/data/incubators.json
Outdated
| "description": "organisme abp space créer pour aider et d’accompagner les startups sa mission principale est d’assister les porteurs du projets innovants et technologique, elle se trouve en plein centre ville de bejaia, dispose des moyens humains comme des experts, des formateurs et du materiels pour accomplir sa mission noble, de dévelloper la region et l’écosysteme économique pour accélérer le processus de devl", | ||
| "email": "LO.MOUSSOUNI@ABP-SPACE.COM", | ||
| "phone": "0660510807", | ||
| "website": "HTTPS://WWW.ABP-SPACE.COM", |
There was a problem hiding this comment.
Multiple URLs use uppercase 'HTTPS://' protocol which is inconsistent with standard URL formatting. These should be normalized to lowercase 'https://' for consistency across the dataset.
| "website": "HTTPS://WWW.ABP-SPACE.COM", | |
| "website": "https://WWW.ABP-SPACE.COM", |
src/data/incubators.json
Outdated
| "description": "l’incubateur de l’ensa dispose d’un espace de coworking, de 2 salles polyvalentes d’une salle de réunion, d’un atelier de prototypage, d’un meeting room et de plusieurs bureaux individuels à la disposition des incubés. en plus des moyens mis en place par l’école (laboratoires, bibliothèques, station expérimentale, station horticole, ferme centrale et fablab)", | ||
| "email": "manal.nechar@edu.ensa.dz", | ||
| "phone": "0657936143", | ||
| "website": "HTTP://WWW.ensa.dz", |
There was a problem hiding this comment.
URL uses uppercase 'HTTP://WWW' which is inconsistent with standard URL formatting. This should be normalized to lowercase 'http://www' for consistency across the dataset.
| "website": "HTTP://WWW.ensa.dz", | |
| "website": "http://www.ensa.dz", |
| ) : ( | ||
| <></> | ||
| )} | ||
| </div> | ||
|
|
There was a problem hiding this comment.
Empty fragment serves no purpose here. Consider either removing the else clause entirely or using null instead of an empty fragment.
| ) : ( | |
| <></> | |
| )} | |
| </div> | |
| ) : null} | |
| </div> |
Fcmam5
left a comment
There was a problem hiding this comment.
Sorry @HouariZegai for pitching in...
But I think that this PR introduces a lot of changes at once, they should be double checked.
There was a problem hiding this comment.
It'll be safer and cleaner to have a sorting logic. Either we append new entries to the list or we have them sorted alphabetically or by date they were founded
src/data/incubators.json
Outdated
| "incubator_type": "Incubateurs universitaires", | ||
| "address": "Cité 50 lgts B6 N°2 Oued Merzoug Tipaza", | ||
| "email": "amerouane.lmd@cu-tipaza.dz", | ||
| "phone": "0666810772" |
There was a problem hiding this comment.
such entries are incosistent, some have country code while this one doesn't.
For email addresses, it's a bit questionable to have a personal email address instead of an institution's official one.
Additionally, such a contribution removes foundedYear information and mapLocation which is sad :(
There was a problem hiding this comment.
I’ve unified the phone numbers; the corrected version will be included in the next commit.
As for the email addresses, I don’t see an issue, those emails are the ones listed on the university’s website for contacting the incubator director or the responsible person as well as the startup.dz website.
src/data/incubators.json
Outdated
| "city": "Algiers", | ||
| "mapLocation": "https://maps.app.goo.gl/EhQwwiGx64Zee6kt8" | ||
| "name": "ALSTRATO", | ||
| "incubator_type": "Incubateurs privés", |
There was a problem hiding this comment.
It would be more robust to use an enum instead of a magic string.
There was a problem hiding this comment.
We could either do it with id/relashionships as it's done in media and media categories e.g https://github.com/algeria-ecosystem/ecosystem/blob/main/src/data/media_category.json
Or define a JSON schema and have this field marked as enums and force users to use one of the pre-defined values
src/data/incubators.json
Outdated
| "city": "Algiers" | ||
| "name": "ENSM INCUBATOR", | ||
| "incubator_type": "Incubateurs universitaires", | ||
| "address": "المدرسة الوطنية العليا للمناجمنت القطب الجامعي بالقليعة", |
There was a problem hiding this comment.
Personally, I think that entries should be in one language: English.
And if the main maintainer and/or the community wants to have l10n, we have an approach to handle it, but mixing languages here is odd imo
| const faviconUrl = domain ? `https://fetchfavicon.com/i/${domain}?size=64` : ''; | ||
|
|
||
|
|
||
| const getSocialIcon = (url: string) => { |
There was a problem hiding this comment.
There are many clean ways to handle such logic, having a Map, a Switch case..
src/data/incubators.json
Outdated
| "linkedin": "https://linkedin.com/company/crearena", | ||
| "city": "Blida" | ||
| "name": "UTINC4.2", | ||
| "incubator_type": "university", |
There was a problem hiding this comment.
- For
incubator_type, we can usecamelCase. - For other new fields, let’s stick to the currently available ones and avoid introducing new fields to the entity.
- We should keep the existing data and avoid removing any of it.
There was a problem hiding this comment.
The problem is that I can’t collect the foundedYear attribute for all incubators because it’s not available on either startup.dz or the universities’ websites.
There was a problem hiding this comment.
We can make it optional then
Description
I have:
Type of Change
Checklist