diff --git a/apps/user-fe/api/auth.ts b/apps/user-fe/api/auth.ts new file mode 100644 index 0000000..d9f4c76 --- /dev/null +++ b/apps/user-fe/api/auth.ts @@ -0,0 +1,69 @@ +// Authentication related API calls +import { apiClient } from "./client"; + +interface LoginCredentials { + email: string; + password: string; +} + +interface SignupData { + email: string; + password: string; + name: string; +} + +interface OtpVerificationParams { + useremail: string; + otp: string; + device_id: string; + mydeviceid?: string; + mydeviceid2?: string; +} + +export const authApi = { + login: async (credentials: LoginCredentials) => { + const response = await apiClient.post("/auth/login", credentials); + return response.data; + }, + + signup: async (data: SignupData) => { + const response = await apiClient.post("/auth/signup", data); + return response.data; + }, + + logout: async () => { + const response = await apiClient.post("/auth/logout"); + return response.data; + }, + + verifyToken: async () => { + const response = await apiClient.get("/auth/verify"); + return response.data; + }, + + sendOtp: async (phone: string) => { + const response = await apiClient.get(`/get/sendotp`, { + params: { phone }, + }); + return response.data; + }, + + resentOtp: async (phone: string) => { + const formData = new FormData(); + formData.append("mobile", phone); + formData.append("type", "text"); + + const response = await apiClient.post( + `/post/resend_otp_with_call`, + formData + ); + return response.data; + }, + + verifyOtp: async (params: OtpVerificationParams) => { + const response = await apiClient.get(`/get/otpverify`, { + params: params, + }); + return response.data; + }, +}; diff --git a/apps/user-fe/api/client.ts b/apps/user-fe/api/client.ts new file mode 100644 index 0000000..54fd683 --- /dev/null +++ b/apps/user-fe/api/client.ts @@ -0,0 +1,39 @@ +// Base API client setup +import axios from "axios"; + +const API_BASE_URL = + process.env.NEXT_PUBLIC_API_URL || "https://indiasgotlatentapi.akamai.net.in"; + +export const apiClient = axios.create({ + baseURL: API_BASE_URL, + headers: { + "Auth-Key": "appxapi", + }, +}); + +// Request interceptor +apiClient.interceptors.request.use( + (config) => { + const token = localStorage.getItem("token"); + if (token) { + config.headers.Authorization = `${token}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + +// Response interceptor +apiClient.interceptors.response.use( + (response) => response, + (error) => { + if (error.response?.status === 401) { + // Handle unauthorized access + localStorage.removeItem("token"); + window.location.href = "/login"; + } + return Promise.reject(error); + } +); diff --git a/apps/user-fe/api/episodes.ts b/apps/user-fe/api/episodes.ts new file mode 100644 index 0000000..b393572 --- /dev/null +++ b/apps/user-fe/api/episodes.ts @@ -0,0 +1,39 @@ +// Episodes related API calls +import { apiClient } from "./client"; +import { PaginatedResponse } from "./types"; + +export interface Episode { + id: string; + title: string; + thumbnail: string; + episodeNumber: number; + youtubeId: string; + isPremium: boolean; +} + +export const episodesApi = { + getAll: async (page = 1, limit = 10) => { + const response = await apiClient.get>( + "/episodes", + { + params: { page, limit }, + } + ); + return response.data; + }, + + getById: async (id: string) => { + const response = await apiClient.get(`/episodes/${id}`); + return response.data; + }, + + getPremiumEpisodes: async (page = 1, limit = 10) => { + const response = await apiClient.get>( + "/episodes/premium", + { + params: { page, limit }, + } + ); + return response.data; + }, +}; diff --git a/apps/user-fe/api/events.ts b/apps/user-fe/api/events.ts new file mode 100644 index 0000000..62260e5 --- /dev/null +++ b/apps/user-fe/api/events.ts @@ -0,0 +1,37 @@ +// Events related API calls +import { apiClient } from "./client"; +import { PaginatedResponse } from "./types"; + +export interface Event { + id: string; + eventName: string; + date: string; + time: string; + venue: string; + location: string; + eventType: string; + ageRating: string; + reviews: number; + ticketPrice: number; +} + +export const eventsApi = { + getAll: async (page = 1, limit = 10) => { + const response = await apiClient.get>("/events", { + params: { page, limit }, + }); + return response.data; + }, + + getById: async (id: string) => { + const response = await apiClient.get(`/events/${id}`); + return response.data; + }, + + bookTicket: async (eventId: string, quantity: number) => { + const response = await apiClient.post(`/events/${eventId}/book`, { + quantity, + }); + return response.data; + }, +}; diff --git a/apps/user-fe/api/index.ts b/apps/user-fe/api/index.ts new file mode 100644 index 0000000..dcb70c5 --- /dev/null +++ b/apps/user-fe/api/index.ts @@ -0,0 +1,5 @@ +export * from "./auth"; +export * from "./episodes"; +export * from "./events"; +export * from "./types"; +export * from "./user"; diff --git a/apps/user-fe/api/types/index.ts b/apps/user-fe/api/types/index.ts new file mode 100644 index 0000000..5200ccb --- /dev/null +++ b/apps/user-fe/api/types/index.ts @@ -0,0 +1,37 @@ +// Common types for API requests and responses +export interface ApiResponse { + data: T; + message: string; + status: number; +} + +export interface PaginatedResponse { + data: T[]; + total: number; + page: number; + limit: number; +} + +export interface ErrorResponse { + message: string; + status: number; +} + +export interface UserValidationResponse { + status: number; + message: string; + data: { + userid: string; + email: string; + phone: string; + info_1: string; + name: string; + username: string; + is_blank: boolean; + state: string; + app_category: string; + report_url: string; + cd: string; + is_tester: boolean; + }; +} diff --git a/apps/user-fe/api/user.ts b/apps/user-fe/api/user.ts new file mode 100644 index 0000000..fc3c4a1 --- /dev/null +++ b/apps/user-fe/api/user.ts @@ -0,0 +1,34 @@ +import { apiClient } from "./client"; +import { UserValidationResponse } from "./types"; + +export const userApi = { + validateUser: async () => { + try { + const userId = localStorage.getItem("userId"); + if (!userId) { + throw new Error("No user ID found"); + } + + const response = await apiClient.get( + "/get/get_user_dt", + { + params: { userid: userId }, + } + ); + + if (response.data.status === 200) { + return response.data; + } else { + // If validation fails, clean up + localStorage.removeItem("token"); + localStorage.removeItem("userId"); + throw new Error("User validation failed"); + } + } catch (error) { + // Clean up on any error + localStorage.removeItem("token"); + localStorage.removeItem("userId"); + throw error; + } + }, +}; diff --git a/apps/user-fe/app/_assets/index.tsx b/apps/user-fe/app/_assets/index.tsx index 8c91872..8a52168 100644 --- a/apps/user-fe/app/_assets/index.tsx +++ b/apps/user-fe/app/_assets/index.tsx @@ -45,3 +45,5 @@ export const IMAGES = { OtpGirl, curtain, }; + +export { playStore, mobile, appStore, Amic }; diff --git a/apps/user-fe/app/_components/home/joinButton.tsx b/apps/user-fe/app/_components/home/joinButton.tsx index 4a87909..78f4ce7 100644 --- a/apps/user-fe/app/_components/home/joinButton.tsx +++ b/apps/user-fe/app/_components/home/joinButton.tsx @@ -8,9 +8,11 @@ export function JoinButton() { "hover:opacity-90 transition-opacity w-fit shadow-[0_0_15px_rgba(170,130,61,0.3)]" )} > - - Join Latent+ - + + + Join Latent+ + + ); } diff --git a/apps/user-fe/app/_components/home/premiumFeature.tsx b/apps/user-fe/app/_components/home/premiumFeature.tsx index e4ae374..9d50d46 100644 --- a/apps/user-fe/app/_components/home/premiumFeature.tsx +++ b/apps/user-fe/app/_components/home/premiumFeature.tsx @@ -12,8 +12,8 @@ export function PremiumFeatures() { ]; return ( -
-
+
+
{/* Glow effect */}
@@ -45,7 +45,9 @@ export function PremiumFeatures() {
{features.map((feature, index) => (
- +
+ +
- + {/* Conditional rendering of Login Button or Profile */} +
+ {isAuthenticated ? ( + + ) : ( + + )} +
{/* Login Dialog */} setIsLoginOpen(false)} /> diff --git a/apps/user-fe/app/_components/premium/card.tsx b/apps/user-fe/app/_components/premium/card.tsx new file mode 100644 index 0000000..1ebc95e --- /dev/null +++ b/apps/user-fe/app/_components/premium/card.tsx @@ -0,0 +1,244 @@ +"use client"; + +import { useState } from "react"; +import { Button } from "@repo/ui/button"; +import { + Check, + Crown, + Star, + Zap, + Smartphone, + Video, + Users, +} from "lucide-react"; +import { cn } from "@repo/ui/utils"; + +export function PremiumCard() { + const [billingCycle, setBillingCycle] = useState<"monthly" | "yearly">( + "monthly" + ); + + const features = [ + { + icon: