function Dashboard() {
// State for profile data
const [profile, setProfile] = React.useState({
firstName: "",
lastName: "",
level: "1",
rank: "Beginner",
points: "0 pts",
initials: "",
progress: 0,
});
// State for stats cards
const [stats, setStats] = React.useState({
activeListings: { count: "-", change: "--vs last month", percent: 0 },
promotionalListings: { count: "-", change: "--vs last month", percent: 0 },
publishedPosts: { count: "-", change: "--vs last month", percent: 0 },
scheduledPosts: { count: "-", change: "--vs last month", percent: 0 },
});
// State for news
const [news, setNews] = React.useState({ loading: true, items: [] });
// Helper: get cookie by name
function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(";").shift();
return "";
}
// Helper: calculate days ago
function calculateDaysAgo(date) {
if (!date || isNaN(date.getTime())) return "recently";
const now = new Date();
const diffTime = Math.abs(now - date);
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
if (diffDays === 0) return "today";
if (diffDays === 1) return "yesterday";
return diffDays + " days ago";
}
// Fetch profile data
React.useEffect(() => {
const firstName = getCookie("app_u_first_name") || "";
const lastName = getCookie("app_u_last_name") || "";
const vendorToken = getCookie("app_u_token") || "";
const appName = getCookie("app_u_app_name") || "";
const apiKey = getCookie("get_local_vip_1580295343903_2_apikey") || "";
// Set initials
const initials =
(firstName.charAt(0).toUpperCase() || "") +
(lastName.charAt(0).toUpperCase() || "");
// Fetch vendor points
if (vendorToken && appName && apiKey) {
const toSearch = { vendor_token: vendorToken };
const endpoint = `https://api.owlapplicationbuilder.com/api/entities/${appName}/vendor_points/get_all_en?to_search=${JSON.stringify(
toSearch
)}&page=1&page_size=10&srt=-1`;
fetch(endpoint, {
method: "GET",
headers: {
Accept: "*/*",
"X-API-KEY": apiKey,
},
})
.then((res) => res.json())
.then((responseData) => {
if (
responseData &&
responseData.data &&
responseData.data.length > 0
) {
const vendorData = responseData.data[0];
let progressPercentage = 0;
const score = vendorData.points || 0;
const level = vendorData.level || 1;
if (level === 1) progressPercentage = Math.min((score / 100) * 100, 100);
else if (level === 2) progressPercentage = Math.min((score / 250) * 100, 100);
else if (level === 3) progressPercentage = Math.min((score / 500) * 100, 100);
else progressPercentage = Math.min((score / 1000) * 100, 100);
setProfile({
firstName,
lastName,
initials,
level: vendorData.level || "0",
rank:
vendorData.level
? vendorData.level.charAt(0).toUpperCase() +
vendorData.level.slice(1)
: "Beginner",
points: (vendorData.points ?? "0") + " pts",
progress: progressPercentage,
});
}
});
} else {
setProfile((p) => ({
...p,
firstName,
lastName,
initials,
}));
}
}, []);
// Fetch news/announcements
React.useEffect(() => {
const appName = getCookie("app_u_app_name");
const apiKey =
getCookie("get_local_vip_1580295343903_2_apikey") ||
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhcHBfbmFtZSI6ImdldF9sb2NhbF92aXBfMTU4MDI5NTM0MzkwM18yIiwiaWF0IjoxNzIwNjA1NDkzfQ.bbmbXfYm3uNx_zVJn1w3TNBp7OJhDkWosa9Ik0Ofy8g";
const searchQuery = { status: "published" };
const queryParams = new URLSearchParams({
page: 1,
page_size: 4,
srt: -1,
to_search: JSON.stringify(searchQuery),
});
if (!appName) {
setNews({ loading: false, items: [] });
return;
}
fetch(
`https://api.owlapplicationbuilder.com/api/entities/${appName}/announcements/get_all_en?${queryParams.toString()}`,
{
method: "GET",
headers: {
Accept: "*/*",
"X-API-KEY": apiKey,
},
}
)
.then((res) => res.json())
.then((responseData) => {
if (responseData && Array.isArray(responseData.data)) {
setNews({ loading: false, items: responseData.data });
} else {
setNews({ loading: false, items: [] });
}
})
.catch(() => setNews({ loading: false, items: [] }));
}, []);
// Fetch stats cards
React.useEffect(() => {
const app_name = getCookie("app_u_app_name");
const api_key = getCookie("get_local_vip_1580295343903_2_apikey");
const user_token = getCookie("app_u_token");
if (!app_name || !api_key || !user_token) return;
// Helper for stats
function updateStat(key, currentCount, referenceCount) {
let percentChange = 0;
let changeText = "";
if (referenceCount > 0 && referenceCount !== currentCount) {
percentChange = ((currentCount - referenceCount) / referenceCount) * 100;
const sign = percentChange >= 0 ? "+" : "";
changeText = `${sign}${percentChange.toFixed(1)}% vs last month`;
} else if (currentCount > 0) {
changeText = "+100% vs last month";
percentChange = 100;
} else {
changeText = "0% vs last month";
percentChange = 0;
}
setStats((s) => ({
...s,
[key]: {
count: currentCount,
change: changeText,
percent: Math.max(Math.min(Math.abs(percentChange), 100), 0),
},
}));
}
// Fetch offers
function fetchOffers(searchable, key) {
const searchParams = {
$and: [
{ utoken: user_token },
{ status: "published" },
...(searchable ? [{ searchable: "true" }] : []),
],
};
const queryParams = new URLSearchParams({
search_columns: "token,title",
to_search: JSON.stringify(searchParams),
page: 1,
page_size: 1000,
srt: -1,
});
const url = `https://api.owlapplicationbuilder.com/api/entities/${app_name}/offers/get_all_en?${queryParams.toString()}`;
return fetch(url, {
method: "GET",
headers: {
Accept: "*/*",
"X-API-KEY": api_key,
},
})
.then((res) => res.json())
.then((result) => result.data || [])
.then((data) => {
const currentCount = data.length;
const referenceCount = Math.round(currentCount / 1.3);
updateStat(key, currentCount, referenceCount);
})
.catch(() => {
setStats((s) => ({
...s,
[key]: { count: "Error", change: "Failed to load data", percent: 0 },
}));
});
}
// Fetch posts
function fetchPosts(uploadStatus, key) {
const searchParams = {
$and: [{ user_token: user_token }, { upload: uploadStatus }],
};
const queryParams = new URLSearchParams({
search_columns: "token,title",
to_search: JSON.stringify(searchParams),
page: 1,
page_size: 1000,
srt: -1,
});
const url = `https://api.owlapplicationbuilder.com/api/entities/${app_name}/posts_queue/get_all_en?${queryParams.toString()}`;
return fetch(url, {
method: "GET",
headers: {
Accept: "*/*",
"X-API-KEY": api_key,
},
})
.then((res) => res.json())
.then((result) => result.data || [])
.then((data) => {
const currentCount = data.length;
const referenceCount =
key === "publishedPosts"
? Math.round(currentCount / 1.2)
: Math.round(currentCount / 1.1);
updateStat(key, currentCount, referenceCount);
})
.catch(() => {
setStats((s) => ({
...s,
[key]: { count: "Error", change: "Failed to load data", percent: 0 },
}));
});
}
fetchOffers(true, "activeListings");
fetchOffers(false, "promotionalListings");
fetchPosts("uploaded", "publishedPosts");
fetchPosts("scheduled", "scheduledPosts");
}, []);
// Styles
React.useEffect(() => {
// Add font-awesome and bootstrap if not present
const fa = document.querySelector('link[href*="font-awesome"]');
if (!fa) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href =
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css";
document.head.appendChild(link);
}
const bs = document.querySelector('link[href*="bootstrap"]');
if (!bs) {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href =
"https://files.owlapplicationbuilder.com/assets/css/bootstrap_4.2.1.min.css";
document.head.appendChild(link);
}
}, []);
return (
<>
Welcome to GetLocal.VIP
Your journey to smarter business promotion starts here.
Hi, {profile.firstName} {profile.lastName}
View Profile
Earn Points
Level: {profile.level}
Rank: {profile.rank}
Points: {profile.points}
{stats.activeListings.count}
{stats.activeListings.change}
{stats.promotionalListings.count}
{stats.promotionalListings.change}
{stats.publishedPosts.count}
{stats.publishedPosts.change}
{stats.scheduledPosts.count}
{stats.scheduledPosts.change}
Recommended Actions
Create First Listing
Create an offer, coupon, event, or health care description
Create Listing →
5 min
+50 pts
Manage Social Media
Manage your social accounts and postings.
Manage →
5 min
+50 pts
PRO
Video Content Creation
Showcase your video content to local audiences
Create Video →
5 min
+50 pts
PRO
Post a Help Wanted Job
Find local talent for your growing business
PRO
Create and Promote Your Event
Drive attendance with targeted local marketing
PRO
Upgrade to PRO Plan
Unlock premium tools for maximum local impact
{/* News Section */}
{news.loading ? (
Loading...
) : news.items.length > 0 ? (
news.items.map((announcement, idx) => {
const title = announcement.title || "Untitled Announcement";
const createdDate = new Date(
parseInt(announcement.created_at) ||
parseInt(announcement.createdAt) ||
Date.now()
);
const daysAgo = calculateDaysAgo(createdDate);
return (
);
})
) : (
No News Available
)}
{/* End News Section */}
);
}
render(
)