feat: add terms and privacy pages, replace favorite with share on job cards

This commit is contained in:
Tiago Yamamoto 2026-02-23 20:56:17 -06:00
parent cf761a70e4
commit 1b897eeb8e
5 changed files with 78 additions and 64 deletions

View file

@ -26,6 +26,18 @@ export function Footer() {
{t("footerMain.info.contact")} {t("footerMain.info.contact")}
</Link> </Link>
</li> </li>
<li className="flex items-center gap-2">
<span className="w-1.5 h-1.5 bg-gray-400 rounded-full inline-block"></span>
<Link href="/terms" className="text-sm text-gray-600 hover:text-primary transition-colors">
{t("footerMain.info.terms") || "Termos de Uso"}
</Link>
</li>
<li className="flex items-center gap-2">
<span className="w-1.5 h-1.5 bg-gray-400 rounded-full inline-block"></span>
<Link href="/privacy" className="text-sm text-gray-600 hover:text-primary transition-colors">
{t("footerMain.info.privacy") || "Política de Privacidade"}
</Link>
</li>
</ul> </ul>
</div> </div>

View file

@ -9,14 +9,25 @@ import {
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { import {
MapPin, MapPin,
Briefcase, Briefcase,
Clock, Clock,
Building2, Building2,
Heart, Share2,
Zap, Zap,
Link2,
MessageCircle,
Linkedin,
Twitter,
Facebook
} from "lucide-react"; } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import { motion } from "framer-motion"; import { motion } from "framer-motion";
@ -35,53 +46,27 @@ interface JobCardProps {
export function JobCard({ job, isApplied, applicationStatus }: JobCardProps) { export function JobCard({ job, isApplied, applicationStatus }: JobCardProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const { user } = useAuth(); const { user } = useAuth();
const [isFavorited, setIsFavorited] = useState(false); const handleShare = (platform: string) => {
const [isLoading, setIsLoading] = useState(true); const url = typeof window !== "undefined" ? `${window.location.origin}/jobs/${job.id}` : `https://local.gohorsejobs.com/jobs/${job.id}`;
const notify = useNotify(); const text = `Confira esta vaga: ${job.title} na ${job.company}`;
useEffect(() => { switch (platform) {
const checkFavorite = async () => { case 'whatsapp':
if (!user) { window.open(`https://wa.me/?text=${encodeURIComponent(text + " " + url)}`, '_blank');
setIsLoading(false); break;
return; case 'linkedin':
} window.open(`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`, '_blank');
try { break;
const res = await jobsApi.checkFavorite(job.id); case 'twitter':
setIsFavorited(res.isFavorite); window.open(`https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`, '_blank');
} catch { break;
// User not logged in or error - ignore case 'facebook':
} finally { window.open(`https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`, '_blank');
setIsLoading(false); break;
} case 'copy':
}; navigator.clipboard.writeText(url);
checkFavorite(); notify.success("Link copiado!", "Link da vaga copiado para a área de transferência.");
}, [job.id, user]); break;
const handleFavorite = async () => {
if (isLoading) return;
if (!user) {
notify.error("Erro", "Faça login para salvar vagas.");
return;
}
try {
if (isFavorited) {
await jobsApi.removeFavorite(job.id);
setIsFavorited(false);
} else {
await jobsApi.addFavorite(job.id);
setIsFavorited(true);
notify.info(
t('jobs.favorites.added.title'),
t('jobs.favorites.added.desc', { title: job.title }),
{
actionUrl: "/dashboard/favorites",
actionLabel: t('jobs.favorites.action'),
}
);
}
} catch {
notify.error("Erro", "Faça login para salvar vagas.");
} }
}; };
@ -169,19 +154,30 @@ export function JobCard({ job, isApplied, applicationStatus }: JobCardProps) {
</div> </div>
</div> </div>
<Button <DropdownMenu>
variant="ghost" <DropdownMenuTrigger asChild>
size="icon" <Button variant="ghost" size="icon" className="shrink-0 text-muted-foreground hover:text-primary transition-colors">
onClick={handleFavorite} <Share2 className="h-4 w-4" />
className="shrink-0"
>
<Heart
className={`h-4 w-4 transition-colors ${isFavorited
? "fill-red-500 text-red-500"
: "text-muted-foreground hover:text-red-500"
}`}
/>
</Button> </Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem onClick={() => handleShare('whatsapp')} className="gap-2 cursor-pointer">
<MessageCircle className="h-4 w-4 text-green-600" /> WhatsApp
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleShare('linkedin')} className="gap-2 cursor-pointer">
<Linkedin className="h-4 w-4 text-blue-600" /> LinkedIn
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleShare('twitter')} className="gap-2 cursor-pointer">
<Twitter className="h-4 w-4 text-sky-500" /> Twitter/X
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleShare('facebook')} className="gap-2 cursor-pointer">
<Facebook className="h-4 w-4 text-blue-700" /> Facebook
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleShare('copy')} className="gap-2 cursor-pointer border-t mt-1 pt-1">
<Link2 className="h-4 w-4 text-gray-500" /> Copiar Link
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div> </div>
</CardHeader> </CardHeader>

View file

@ -1379,7 +1379,9 @@
"info": { "info": {
"title": "Information", "title": "Information",
"about": "About Us", "about": "About Us",
"contact": "Contact" "contact": "Contact",
"terms": "Terms of Use",
"privacy": "Privacy Policy"
}, },
"candidates": { "candidates": {
"title": "For Candidates", "title": "For Candidates",

View file

@ -1380,7 +1380,9 @@
"info": { "info": {
"title": "Información", "title": "Información",
"about": "Sobre nosotros", "about": "Sobre nosotros",
"contact": "Contacto" "contact": "Contacto",
"terms": "Términos de Uso",
"privacy": "Política de Privacidad"
}, },
"candidates": { "candidates": {
"title": "Para Candidatos", "title": "Para Candidatos",

View file

@ -1386,9 +1386,11 @@
}, },
"footerMain": { "footerMain": {
"info": { "info": {
"title": "Informações", "title": "Informação",
"about": "Sobre nós", "about": "Sobre nós",
"contact": "Contato" "contact": "Contato",
"terms": "Termos de Uso",
"privacy": "Política de Privacidade"
}, },
"candidates": { "candidates": {
"title": "Para Candidatos", "title": "Para Candidatos",