+
-
- Portal de Empregos
+ GoHorse Jobs
- Conectamos candidatos e empresas de forma rápida e direta. Encontre sua próxima oportunidade profissional.
+ Conectamos candidatos e empresas de forma rápida e direta. Encontre sua próxima oportunidade profissional em tecnologia.
-
Empresa
+
Vagas por Tecnologia
-
-
- Sobre
+
+ Desenvolvedor Python
-
-
- Contato
+
+ Desenvolvedor React
-
-
- Carreiras
+
+ Analista de Dados
+
+
+ -
+
+ Vagas Remotas
-
Privacidade e Termos
+
Empresa
-
-
+
+ Sobre
+
+
+ -
+
+ Contato
+
+
+ -
+
+ Todas as Vagas
+
+
+
+
+
+
+
Legal
+
+ -
+
Política de Privacidade
-
-
+
Termos de Uso
@@ -55,9 +79,10 @@ export function Footer() {
-
© 2025 Portal de Empregos. Todos os direitos reservados.
+
© 2025 GoHorse Jobs. Todos os direitos reservados.
)
}
+
diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts
index 0da010e..8ef64ce 100644
--- a/frontend/src/lib/api.ts
+++ b/frontend/src/lib/api.ts
@@ -97,6 +97,94 @@ export const companiesApi = {
};
// Jobs API (public)
+export interface ApiJob {
+ id: number;
+ companyId: number;
+ createdBy: number;
+ title: string;
+ description: string;
+ salaryMin?: number;
+ salaryMax?: number;
+ salaryType?: string;
+ employmentType?: string;
+ workingHours?: string;
+ location?: string;
+ regionId?: number;
+ cityId?: number;
+ requirements?: Record
| string[];
+ benefits?: Record | string[];
+ visaSupport: boolean;
+ languageLevel?: string;
+ status: string;
+ createdAt: string;
+ updatedAt: string;
+ companyName?: string;
+ companyLogoUrl?: string;
+ regionName?: string;
+ cityName?: string;
+}
+
+export interface PaginatedResponse {
+ data: T[];
+ pagination: {
+ page: number;
+ limit: number;
+ total: number;
+ };
+}
+
export const jobsApi = {
- list: () => apiRequest("/jobs"),
+ list: (params?: { page?: number; limit?: number; companyId?: number }) => {
+ const query = new URLSearchParams();
+ if (params?.page) query.set('page', String(params.page));
+ if (params?.limit) query.set('limit', String(params.limit));
+ if (params?.companyId) query.set('companyId', String(params.companyId));
+ const queryStr = query.toString();
+ return apiRequest>(`/jobs${queryStr ? `?${queryStr}` : ''}`);
+ },
+
+ getById: (id: number) => apiRequest(`/jobs/${id}`),
};
+
+// Transform API job to frontend Job format
+export function transformApiJobToFrontend(apiJob: ApiJob): import('./types').Job {
+ // Format salary
+ let salary: string | undefined;
+ if (apiJob.salaryMin && apiJob.salaryMax) {
+ salary = `R$ ${apiJob.salaryMin.toLocaleString('pt-BR')} - R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`;
+ } else if (apiJob.salaryMin) {
+ salary = `A partir de R$ ${apiJob.salaryMin.toLocaleString('pt-BR')}`;
+ } else if (apiJob.salaryMax) {
+ salary = `Até R$ ${apiJob.salaryMax.toLocaleString('pt-BR')}`;
+ }
+
+ // Determine type
+ type JobType = 'full-time' | 'part-time' | 'contract' | 'Remoto' | 'Tempo Integral';
+ let type: JobType = 'Tempo Integral';
+ if (apiJob.employmentType === 'full-time') type = 'Tempo Integral';
+ else if (apiJob.employmentType === 'part-time') type = 'part-time';
+ else if (apiJob.employmentType === 'contract') type = 'contract';
+ else if (apiJob.location?.toLowerCase().includes('remoto')) type = 'Remoto';
+
+ // Extract requirements
+ const requirements: string[] = [];
+ if (apiJob.requirements) {
+ if (Array.isArray(apiJob.requirements)) {
+ requirements.push(...apiJob.requirements.map(String));
+ } else if (typeof apiJob.requirements === 'object') {
+ Object.values(apiJob.requirements).forEach(v => requirements.push(String(v)));
+ }
+ }
+
+ return {
+ id: String(apiJob.id),
+ title: apiJob.title,
+ company: apiJob.companyName || 'Empresa',
+ location: apiJob.location || apiJob.cityName || 'Localização não informada',
+ type,
+ salary,
+ description: apiJob.description,
+ requirements: requirements.length > 0 ? requirements : ['Ver detalhes'],
+ postedAt: apiJob.createdAt?.split('T')[0] || new Date().toISOString().split('T')[0],
+ };
+}
diff --git a/JobScraper_MultiSite/README.md b/job-scraper-multisite/README.md
similarity index 100%
rename from JobScraper_MultiSite/README.md
rename to job-scraper-multisite/README.md
diff --git a/JobScraper_MultiSite/main_scraper.py b/job-scraper-multisite/main_scraper.py
similarity index 100%
rename from JobScraper_MultiSite/main_scraper.py
rename to job-scraper-multisite/main_scraper.py
diff --git a/JobScraper_MultiSite/output/.gitkeep b/job-scraper-multisite/output/.gitkeep
similarity index 100%
rename from JobScraper_MultiSite/output/.gitkeep
rename to job-scraper-multisite/output/.gitkeep
diff --git a/JobScraper_MultiSite/requirements.txt b/job-scraper-multisite/requirements.txt
similarity index 100%
rename from JobScraper_MultiSite/requirements.txt
rename to job-scraper-multisite/requirements.txt
diff --git a/JobScraper_MultiSite/scrapers/__init__.py b/job-scraper-multisite/scrapers/__init__.py
similarity index 100%
rename from JobScraper_MultiSite/scrapers/__init__.py
rename to job-scraper-multisite/scrapers/__init__.py
diff --git a/JobScraper_MultiSite/scrapers/geekhunter_scraper.py b/job-scraper-multisite/scrapers/geekhunter_scraper.py
similarity index 100%
rename from JobScraper_MultiSite/scrapers/geekhunter_scraper.py
rename to job-scraper-multisite/scrapers/geekhunter_scraper.py
diff --git a/JobScraper_MultiSite/scrapers/programathor_scraper.py b/job-scraper-multisite/scrapers/programathor_scraper.py
similarity index 100%
rename from JobScraper_MultiSite/scrapers/programathor_scraper.py
rename to job-scraper-multisite/scrapers/programathor_scraper.py