From 0925ce59ce9cb40e53470f8ba638fbf0309157ab Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 18 Jun 2026 22:59:48 +0000 Subject: [PATCH] Autosave: 20260618-225950 --- frontend/src/components/AsideMenuLayer.tsx | 5 +- frontend/src/components/NavBarItem.tsx | 3 +- frontend/src/config.ts | 2 +- frontend/src/layouts/Authenticated.tsx | 3 +- frontend/src/menuAside.ts | 392 ++++-- frontend/src/menuNavBar.ts | 20 +- frontend/src/pages/_app.tsx | 6 +- frontend/src/pages/dashboard.tsx | 1341 +------------------ frontend/src/pages/error.tsx | 2 +- frontend/src/pages/index.tsx | 938 ++++++++++++-- frontend/src/pages/login.tsx | 4 +- frontend/src/pages/mega-super-app.tsx | 660 ++++++++++ frontend/src/pages/privacy-policy.tsx | 2 +- frontend/src/pages/search.tsx | 6 +- frontend/src/pages/terms-of-use.tsx | 2 +- frontend/src/pages/vorta-commerce.tsx | 1362 ++++++++++++++++++++ frontend/src/pages/vorta-synapse.tsx | 800 ++++++++++++ 17 files changed, 3911 insertions(+), 1637 deletions(-) create mode 100644 frontend/src/pages/mega-super-app.tsx create mode 100644 frontend/src/pages/vorta-commerce.tsx create mode 100644 frontend/src/pages/vorta-synapse.tsx diff --git a/frontend/src/components/AsideMenuLayer.tsx b/frontend/src/components/AsideMenuLayer.tsx index da31d39..edf0ddf 100644 --- a/frontend/src/components/AsideMenuLayer.tsx +++ b/frontend/src/components/AsideMenuLayer.tsx @@ -3,10 +3,9 @@ import { mdiLogout, mdiClose } from '@mdi/js' import BaseIcon from './BaseIcon' import AsideMenuList from './AsideMenuList' import { MenuAsideItem } from '../interfaces' -import { useAppSelector } from '../stores/hooks' +import { useAppDispatch, useAppSelector } from '../stores/hooks' import Link from 'next/link'; -import { useAppDispatch } from '../stores/hooks'; import { createAsyncThunk } from '@reduxjs/toolkit'; import axios from 'axios'; @@ -68,7 +67,7 @@ export default function AsideMenuLayer({ menu, className = '', ...props }: Props >
- Vorta Universe MVP + VORTA-COMMERCE {organizationName &&

{organizationName}

} diff --git a/frontend/src/components/NavBarItem.tsx b/frontend/src/components/NavBarItem.tsx index eb155e3..fb0fca2 100644 --- a/frontend/src/components/NavBarItem.tsx +++ b/frontend/src/components/NavBarItem.tsx @@ -1,6 +1,5 @@ -import React, {useEffect, useRef} from 'react' +import React, { useEffect, useRef, useState } from 'react' import Link from 'next/link' -import { useState } from 'react' import { mdiChevronUp, mdiChevronDown } from '@mdi/js' import BaseDivider from './BaseDivider' import BaseIcon from './BaseIcon' diff --git a/frontend/src/config.ts b/frontend/src/config.ts index a9783c8..3424a08 100644 --- a/frontend/src/config.ts +++ b/frontend/src/config.ts @@ -8,7 +8,7 @@ export const localStorageStyleKey = 'style' export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20' -export const appTitle = 'created by Flatlogic generator!' +export const appTitle = 'Ekosistem Kepercayaan Digital' export const getPageTitle = (currentPageTitle: string) => `${currentPageTitle} — ${appTitle}` diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx index 1b9907d..73d8391 100644 --- a/frontend/src/layouts/Authenticated.tsx +++ b/frontend/src/layouts/Authenticated.tsx @@ -1,5 +1,4 @@ -import React, { ReactNode, useEffect } from 'react' -import { useState } from 'react' +import React, { ReactNode, useEffect, useState } from 'react' import jwt from 'jsonwebtoken'; import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js' import menuAside from '../menuAside' diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts index 0b796bc..4898aad 100644 --- a/frontend/src/menuAside.ts +++ b/frontend/src/menuAside.ts @@ -1,355 +1,487 @@ import * as icon from '@mdi/js'; -import { MenuAsideItem } from './interfaces' +import { MenuAsideItem } from './interfaces'; const menuAside: MenuAsideItem[] = [ { - href: '/dashboard', - icon: icon.mdiViewDashboardOutline, - label: 'Dashboard', + href: '/vorta-commerce', + icon: + 'mdiStorefrontOutline' in icon + ? (icon['mdiStorefrontOutline' as keyof typeof icon] as string) + : icon.mdiViewDashboardOutline, + label: 'VORTA-Commerce', }, - + + { + href: '/mega-super-app', + label: 'Mega Super-App', + icon: + 'mdiApps' in icon + ? (icon['mdiApps' as keyof typeof icon] as string) + : icon.mdiViewDashboardOutline, + }, + + + { + href: '/vorta-synapse', + label: '04 Vorta Synapse', + icon: + 'mdiTransitConnectionVariant' in icon + ? (icon['mdiTransitConnectionVariant' as keyof typeof icon] as string) + : icon.mdiViewDashboardOutline, + }, + { href: '/users/users-list', - label: 'Users', + label: 'Pengguna', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore icon: icon.mdiAccountGroup ?? icon.mdiTable, - permissions: 'READ_USERS' + permissions: 'READ_USERS', }, { href: '/roles/roles-list', - label: 'Roles', + label: 'Peran', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable, - permissions: 'READ_ROLES' + permissions: 'READ_ROLES', }, { href: '/permissions/permissions-list', - label: 'Permissions', + label: 'Izin Akses', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore icon: icon.mdiShieldAccountOutline ?? icon.mdiTable, - permissions: 'READ_PERMISSIONS' + permissions: 'READ_PERMISSIONS', }, { href: '/organizations/organizations-list', - label: 'Organizations', + label: 'Organisasi', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore icon: icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_ORGANIZATIONS' + permissions: 'READ_ORGANIZATIONS', }, { href: '/trust_profiles/trust_profiles-list', - label: 'Trust profiles', + label: 'Profil Kepercayaan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiShieldCheck' in icon ? icon['mdiShieldCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_TRUST_PROFILES' + icon: + 'mdiShieldCheck' in icon + ? icon['mdiShieldCheck' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_TRUST_PROFILES', }, { href: '/identity_verifications/identity_verifications-list', - label: 'Identity verifications', + label: 'Verifikasi Identitas', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiCardAccountDetailsOutline' in icon ? icon['mdiCardAccountDetailsOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_IDENTITY_VERIFICATIONS' + icon: + 'mdiCardAccountDetailsOutline' in icon + ? icon['mdiCardAccountDetailsOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_IDENTITY_VERIFICATIONS', }, { href: '/wallets/wallets-list', - label: 'Wallets', + label: 'Dompet', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiWallet' in icon ? icon['mdiWallet' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_WALLETS' + icon: + 'mdiWallet' in icon + ? icon['mdiWallet' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_WALLETS', }, { href: '/wallet_transactions/wallet_transactions-list', - label: 'Wallet transactions', + label: 'Transaksi Dompet', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiSwapHorizontal' in icon ? icon['mdiSwapHorizontal' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_WALLET_TRANSACTIONS' + icon: + 'mdiSwapHorizontal' in icon + ? icon['mdiSwapHorizontal' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_WALLET_TRANSACTIONS', }, { href: '/chats/chats-list', - label: 'Chats', + label: 'Percakapan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiChat' in icon ? icon['mdiChat' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_CHATS' + icon: + 'mdiChat' in icon + ? icon['mdiChat' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_CHATS', }, { href: '/chat_participants/chat_participants-list', - label: 'Chat participants', + label: 'Peserta Percakapan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiAccountGroup' in icon ? icon['mdiAccountGroup' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_CHAT_PARTICIPANTS' + icon: + 'mdiAccountGroup' in icon + ? icon['mdiAccountGroup' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_CHAT_PARTICIPANTS', }, { href: '/messages/messages-list', - label: 'Messages', + label: 'Pesan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiMessageTextOutline' in icon ? icon['mdiMessageTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_MESSAGES' + icon: + 'mdiMessageTextOutline' in icon + ? icon['mdiMessageTextOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_MESSAGES', }, { href: '/stores/stores-list', - label: 'Stores', + label: 'Toko', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiStore' in icon ? icon['mdiStore' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_STORES' + icon: + 'mdiStore' in icon + ? icon['mdiStore' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_STORES', }, { href: '/addresses/addresses-list', - label: 'Addresses', + label: 'Alamat', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiMapMarker' in icon ? icon['mdiMapMarker' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_ADDRESSES' + icon: + 'mdiMapMarker' in icon + ? icon['mdiMapMarker' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_ADDRESSES', }, { href: '/product_categories/product_categories-list', - label: 'Product categories', + label: 'Kategori Produk', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiShapeOutline' in icon ? icon['mdiShapeOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_PRODUCT_CATEGORIES' + icon: + 'mdiShapeOutline' in icon + ? icon['mdiShapeOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_PRODUCT_CATEGORIES', }, { href: '/products/products-list', - label: 'Products', + label: 'Produk', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiPackageVariantClosed' in icon ? icon['mdiPackageVariantClosed' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_PRODUCTS' + icon: + 'mdiPackageVariantClosed' in icon + ? icon['mdiPackageVariantClosed' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_PRODUCTS', }, { href: '/orders/orders-list', - label: 'Orders', + label: 'Pesanan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiReceiptTextOutline' in icon ? icon['mdiReceiptTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_ORDERS' + icon: + 'mdiReceiptTextOutline' in icon + ? icon['mdiReceiptTextOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_ORDERS', }, { href: '/order_items/order_items-list', - label: 'Order items', + label: 'Item Pesanan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiFormatListBulleted' in icon ? icon['mdiFormatListBulleted' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_ORDER_ITEMS' + icon: + 'mdiFormatListBulleted' in icon + ? icon['mdiFormatListBulleted' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_ORDER_ITEMS', }, { href: '/payments/payments-list', - label: 'Payments', + label: 'Pembayaran', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiCreditCardOutline' in icon ? icon['mdiCreditCardOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_PAYMENTS' + icon: + 'mdiCreditCardOutline' in icon + ? icon['mdiCreditCardOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_PAYMENTS', }, { href: '/product_reviews/product_reviews-list', - label: 'Product reviews', + label: 'Ulasan Produk', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiStarCircleOutline' in icon ? icon['mdiStarCircleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_PRODUCT_REVIEWS' + icon: + 'mdiStarCircleOutline' in icon + ? icon['mdiStarCircleOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_PRODUCT_REVIEWS', }, { href: '/shipments/shipments-list', - label: 'Shipments', + label: 'Pengiriman', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiTruckDeliveryOutline' in icon ? icon['mdiTruckDeliveryOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_SHIPMENTS' + icon: + 'mdiTruckDeliveryOutline' in icon + ? icon['mdiTruckDeliveryOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_SHIPMENTS', }, { href: '/posts/posts-list', - label: 'Posts', + label: 'Postingan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiPlayCircleOutline' in icon ? icon['mdiPlayCircleOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_POSTS' + icon: + 'mdiPlayCircleOutline' in icon + ? icon['mdiPlayCircleOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_POSTS', }, { href: '/deep_dive_links/deep_dive_links-list', - label: 'Deep dive links', + label: 'Tautan Deep Dive', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiLinkVariant' in icon ? icon['mdiLinkVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_DEEP_DIVE_LINKS' + icon: + 'mdiLinkVariant' in icon + ? icon['mdiLinkVariant' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_DEEP_DIVE_LINKS', }, { href: '/citations/citations-list', - label: 'Citations', + label: 'Sitasi', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiFileDocumentCheckOutline' in icon ? icon['mdiFileDocumentCheckOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_CITATIONS' + icon: + 'mdiFileDocumentCheckOutline' in icon + ? icon['mdiFileDocumentCheckOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_CITATIONS', }, { href: '/post_engagements/post_engagements-list', - label: 'Post engagements', + label: 'Interaksi Postingan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiThumbUpOutline' in icon ? icon['mdiThumbUpOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_POST_ENGAGEMENTS' + icon: + 'mdiThumbUpOutline' in icon + ? icon['mdiThumbUpOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_POST_ENGAGEMENTS', }, { href: '/rewards/rewards-list', - label: 'Rewards', + label: 'Hadiah', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiTrophyOutline' in icon ? icon['mdiTrophyOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_REWARDS' + icon: + 'mdiTrophyOutline' in icon + ? icon['mdiTrophyOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_REWARDS', }, { href: '/forum_spaces/forum_spaces-list', - label: 'Forum spaces', + label: 'Ruang Forum', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiForumOutline' in icon ? icon['mdiForumOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_FORUM_SPACES' + icon: + 'mdiForumOutline' in icon + ? icon['mdiForumOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_FORUM_SPACES', }, { href: '/forum_threads/forum_threads-list', - label: 'Forum threads', + label: 'Topik Forum', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiMessageBulleted' in icon ? icon['mdiMessageBulleted' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_FORUM_THREADS' + icon: + 'mdiMessageBulleted' in icon + ? icon['mdiMessageBulleted' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_FORUM_THREADS', }, { href: '/forum_posts/forum_posts-list', - label: 'Forum posts', + label: 'Posting Forum', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiReplyOutline' in icon ? icon['mdiReplyOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_FORUM_POSTS' + icon: + 'mdiReplyOutline' in icon + ? icon['mdiReplyOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_FORUM_POSTS', }, { href: '/job_companies/job_companies-list', - label: 'Job companies', + label: 'Perusahaan Lowongan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiOfficeBuildingOutline' in icon ? icon['mdiOfficeBuildingOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_JOB_COMPANIES' + icon: + 'mdiOfficeBuildingOutline' in icon + ? icon['mdiOfficeBuildingOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_JOB_COMPANIES', }, { href: '/job_listings/job_listings-list', - label: 'Job listings', + label: 'Daftar Lowongan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiBriefcaseOutline' in icon ? icon['mdiBriefcaseOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_JOB_LISTINGS' + icon: + 'mdiBriefcaseOutline' in icon + ? icon['mdiBriefcaseOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_JOB_LISTINGS', }, { href: '/job_applications/job_applications-list', - label: 'Job applications', + label: 'Lamaran Kerja', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiFileAccountOutline' in icon ? icon['mdiFileAccountOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_JOB_APPLICATIONS' + icon: + 'mdiFileAccountOutline' in icon + ? icon['mdiFileAccountOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_JOB_APPLICATIONS', }, { href: '/user_credentials/user_credentials-list', - label: 'User credentials', + label: 'Kredensial Pengguna', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiCertificateOutline' in icon ? icon['mdiCertificateOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_USER_CREDENTIALS' + icon: + 'mdiCertificateOutline' in icon + ? icon['mdiCertificateOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_USER_CREDENTIALS', }, { href: '/affiliate_programs/affiliate_programs-list', - label: 'Affiliate programs', + label: 'Program Afiliasi', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiAccountNetworkOutline' in icon ? icon['mdiAccountNetworkOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_AFFILIATE_PROGRAMS' + icon: + 'mdiAccountNetworkOutline' in icon + ? icon['mdiAccountNetworkOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_AFFILIATE_PROGRAMS', }, { href: '/affiliate_memberships/affiliate_memberships-list', - label: 'Affiliate memberships', + label: 'Keanggotaan Afiliasi', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiLinkBoxOutline' in icon ? icon['mdiLinkBoxOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_AFFILIATE_MEMBERSHIPS' + icon: + 'mdiLinkBoxOutline' in icon + ? icon['mdiLinkBoxOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_AFFILIATE_MEMBERSHIPS', }, { href: '/commissions/commissions-list', - label: 'Commissions', + label: 'Komisi', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiCashCheck' in icon ? icon['mdiCashCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_COMMISSIONS' + icon: + 'mdiCashCheck' in icon + ? icon['mdiCashCheck' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_COMMISSIONS', }, { href: '/geo_rules/geo_rules-list', - label: 'Geo rules', + label: 'Aturan Geo', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiMapMarkerRadiusOutline' in icon ? icon['mdiMapMarkerRadiusOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_GEO_RULES' + icon: + 'mdiMapMarkerRadiusOutline' in icon + ? icon['mdiMapMarkerRadiusOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_GEO_RULES', }, { href: '/facta_queries/facta_queries-list', - label: 'Facta queries', + label: 'Kueri Facta', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiMagnify' in icon ? icon['mdiMagnify' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_FACTA_QUERIES' + icon: + 'mdiMagnify' in icon + ? icon['mdiMagnify' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_FACTA_QUERIES', }, { href: '/facta_answers/facta_answers-list', - label: 'Facta answers', + label: 'Jawaban Facta', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiShieldSearchOutline' in icon ? icon['mdiShieldSearchOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_FACTA_ANSWERS' + icon: + 'mdiShieldSearchOutline' in icon + ? icon['mdiShieldSearchOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_FACTA_ANSWERS', }, { href: '/subscriptions/subscriptions-list', - label: 'Subscriptions', + label: 'Langganan', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiCrownOutline' in icon ? icon['mdiCrownOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_SUBSCRIPTIONS' + icon: + 'mdiCrownOutline' in icon + ? icon['mdiCrownOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_SUBSCRIPTIONS', }, { href: '/api_clients/api_clients-list', - label: 'Api clients', + label: 'Klien API', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiApi' in icon ? icon['mdiApi' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_API_CLIENTS' + icon: + 'mdiApi' in icon + ? icon['mdiApi' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_API_CLIENTS', }, { href: '/audit_events/audit_events-list', - label: 'Audit events', + label: 'Peristiwa Audit', // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - icon: 'mdiClipboardTextOutline' in icon ? icon['mdiClipboardTextOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable, - permissions: 'READ_AUDIT_EVENTS' + icon: + 'mdiClipboardTextOutline' in icon + ? icon['mdiClipboardTextOutline' as keyof typeof icon] + : (icon.mdiTable ?? icon.mdiTable), + permissions: 'READ_AUDIT_EVENTS', }, { href: '/profile', - label: 'Profile', + label: 'Profil', icon: icon.mdiAccountCircle, }, - { href: '/api-docs', target: '_blank', - label: 'Swagger API', + label: 'Dokumentasi API', icon: icon.mdiFileCode, - permissions: 'READ_API_DOCS' + permissions: 'READ_API_DOCS', }, -] +]; -export default menuAside +export default menuAside; diff --git a/frontend/src/menuNavBar.ts b/frontend/src/menuNavBar.ts index a5dd956..ca1b7b1 100644 --- a/frontend/src/menuNavBar.ts +++ b/frontend/src/menuNavBar.ts @@ -10,8 +10,8 @@ import { mdiThemeLightDark, mdiGithub, mdiVuejs, -} from '@mdi/js' -import { MenuNavBarItem } from './interfaces' +} from '@mdi/js'; +import { MenuNavBarItem } from './interfaces'; const menuNavBar: MenuNavBarItem[] = [ { @@ -19,7 +19,7 @@ const menuNavBar: MenuNavBarItem[] = [ menu: [ { icon: mdiAccount, - label: 'My Profile', + label: 'Profil Saya', href: '/profile', }, { @@ -27,27 +27,25 @@ const menuNavBar: MenuNavBarItem[] = [ }, { icon: mdiLogout, - label: 'Log Out', + label: 'Keluar', isLogout: true, }, ], }, { icon: mdiThemeLightDark, - label: 'Light/Dark', + label: 'Terang/Gelap', isDesktopNoLabel: true, isToggleLightDark: true, }, { icon: mdiLogout, - label: 'Log out', + label: 'Keluar', isDesktopNoLabel: true, isLogout: true, }, -] - -export const webPagesNavBar = [ - ]; -export default menuNavBar +export const webPagesNavBar = []; + +export default menuNavBar; diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 6f3ba90..97ade84 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -121,7 +121,7 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { setSteps(loginSteps); setStepName('loginSteps'); setStepsEnabled(true); - }else if (router.pathname === '/dashboard' && !isCompleted('appSteps')) { + }else if (router.pathname === '/vorta-commerce' && !isCompleted('appSteps')) { setTimeout(() => { setSteps(appSteps); setStepName('appSteps'); @@ -149,8 +149,8 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { setStepsEnabled(false); }; - const title = 'Vorta Universe MVP' - const description = "Unified super-app for verified identity, chat-to-commerce, and citation-backed search with a single wallet." + const title = 'VORTA-COMMERCE' + const description = "Commerce operating system for products, inventory, orders, payments, shipping, marketing, reports, and settings." const url = "https://flatlogic.com/" const image = "https://project-screens.s3.amazonaws.com/screenshots/40285/app-hero-20260618-210659.png" const imageWidth = '1920' diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx index d7e733c..db3922e 100644 --- a/frontend/src/pages/dashboard.tsx +++ b/frontend/src/pages/dashboard.tsx @@ -1,1340 +1,3 @@ -import * as icon from '@mdi/js'; -import Head from 'next/head' -import React from 'react' -import axios from 'axios'; -import type { ReactElement } from 'react' -import LayoutAuthenticated from '../layouts/Authenticated' -import SectionMain from '../components/SectionMain' -import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' -import BaseIcon from "../components/BaseIcon"; -import { getPageTitle } from '../config' -import Link from "next/link"; +import VortaCommercePage from './vorta-commerce'; -import { hasPermission } from "../helpers/userPermissions"; -import { fetchWidgets } from '../stores/roles/rolesSlice'; -import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator'; -import { SmartWidget } from '../components/SmartWidget/SmartWidget'; - -import { useAppDispatch, useAppSelector } from '../stores/hooks'; -const Dashboard = () => { - const dispatch = useAppDispatch(); - const iconsColor = useAppSelector((state) => state.style.iconsColor); - const corners = useAppSelector((state) => state.style.corners); - const cardsStyle = useAppSelector((state) => state.style.cardsStyle); - - const loadingMessage = 'Loading...'; - - - const [users, setUsers] = React.useState(loadingMessage); - const [roles, setRoles] = React.useState(loadingMessage); - const [permissions, setPermissions] = React.useState(loadingMessage); - const [organizations, setOrganizations] = React.useState(loadingMessage); - const [trust_profiles, setTrust_profiles] = React.useState(loadingMessage); - const [identity_verifications, setIdentity_verifications] = React.useState(loadingMessage); - const [wallets, setWallets] = React.useState(loadingMessage); - const [wallet_transactions, setWallet_transactions] = React.useState(loadingMessage); - const [chats, setChats] = React.useState(loadingMessage); - const [chat_participants, setChat_participants] = React.useState(loadingMessage); - const [messages, setMessages] = React.useState(loadingMessage); - const [stores, setStores] = React.useState(loadingMessage); - const [addresses, setAddresses] = React.useState(loadingMessage); - const [product_categories, setProduct_categories] = React.useState(loadingMessage); - const [products, setProducts] = React.useState(loadingMessage); - const [orders, setOrders] = React.useState(loadingMessage); - const [order_items, setOrder_items] = React.useState(loadingMessage); - const [payments, setPayments] = React.useState(loadingMessage); - const [product_reviews, setProduct_reviews] = React.useState(loadingMessage); - const [shipments, setShipments] = React.useState(loadingMessage); - const [posts, setPosts] = React.useState(loadingMessage); - const [deep_dive_links, setDeep_dive_links] = React.useState(loadingMessage); - const [citations, setCitations] = React.useState(loadingMessage); - const [post_engagements, setPost_engagements] = React.useState(loadingMessage); - const [rewards, setRewards] = React.useState(loadingMessage); - const [forum_spaces, setForum_spaces] = React.useState(loadingMessage); - const [forum_threads, setForum_threads] = React.useState(loadingMessage); - const [forum_posts, setForum_posts] = React.useState(loadingMessage); - const [job_companies, setJob_companies] = React.useState(loadingMessage); - const [job_listings, setJob_listings] = React.useState(loadingMessage); - const [job_applications, setJob_applications] = React.useState(loadingMessage); - const [user_credentials, setUser_credentials] = React.useState(loadingMessage); - const [affiliate_programs, setAffiliate_programs] = React.useState(loadingMessage); - const [affiliate_memberships, setAffiliate_memberships] = React.useState(loadingMessage); - const [commissions, setCommissions] = React.useState(loadingMessage); - const [geo_rules, setGeo_rules] = React.useState(loadingMessage); - const [facta_queries, setFacta_queries] = React.useState(loadingMessage); - const [facta_answers, setFacta_answers] = React.useState(loadingMessage); - const [subscriptions, setSubscriptions] = React.useState(loadingMessage); - const [api_clients, setApi_clients] = React.useState(loadingMessage); - const [audit_events, setAudit_events] = React.useState(loadingMessage); - - - const [widgetsRole, setWidgetsRole] = React.useState({ - role: { value: '', label: '' }, - }); - const { currentUser } = useAppSelector((state) => state.auth); - const { isFetchingQuery } = useAppSelector((state) => state.openAi); - - const { rolesWidgets, loading } = useAppSelector((state) => state.roles); - - - const organizationId = currentUser?.organizations?.id; - - async function loadData() { - const entities = ['users','roles','permissions','organizations','trust_profiles','identity_verifications','wallets','wallet_transactions','chats','chat_participants','messages','stores','addresses','product_categories','products','orders','order_items','payments','product_reviews','shipments','posts','deep_dive_links','citations','post_engagements','rewards','forum_spaces','forum_threads','forum_posts','job_companies','job_listings','job_applications','user_credentials','affiliate_programs','affiliate_memberships','commissions','geo_rules','facta_queries','facta_answers','subscriptions','api_clients','audit_events',]; - const fns = [setUsers,setRoles,setPermissions,setOrganizations,setTrust_profiles,setIdentity_verifications,setWallets,setWallet_transactions,setChats,setChat_participants,setMessages,setStores,setAddresses,setProduct_categories,setProducts,setOrders,setOrder_items,setPayments,setProduct_reviews,setShipments,setPosts,setDeep_dive_links,setCitations,setPost_engagements,setRewards,setForum_spaces,setForum_threads,setForum_posts,setJob_companies,setJob_listings,setJob_applications,setUser_credentials,setAffiliate_programs,setAffiliate_memberships,setCommissions,setGeo_rules,setFacta_queries,setFacta_answers,setSubscriptions,setApi_clients,setAudit_events,]; - - const requests = entities.map((entity, index) => { - - if(hasPermission(currentUser, `READ_${entity.toUpperCase()}`)) { - return axios.get(`/${entity.toLowerCase()}/count`); - } else { - fns[index](null); - return Promise.resolve({data: {count: null}}); - } - - }); - - Promise.allSettled(requests).then((results) => { - results.forEach((result, i) => { - if (result.status === 'fulfilled') { - fns[i](result.value.data.count); - } else { - fns[i](result.reason.message); - } - }); - }); - } - - async function getWidgets(roleId) { - await dispatch(fetchWidgets(roleId)); - } - React.useEffect(() => { - if (!currentUser) return; - loadData().then(); - setWidgetsRole({ role: { value: currentUser?.app_role?.id, label: currentUser?.app_role?.name } }); - }, [currentUser]); - - React.useEffect(() => { - if (!currentUser || !widgetsRole?.role?.value) return; - getWidgets(widgetsRole?.role?.value || '').then(); - }, [widgetsRole?.role?.value]); - - return ( - <> - - - {getPageTitle('Overview')} - - - - - {''} - - - {hasPermission(currentUser, 'CREATE_ROLES') && } - {!!rolesWidgets.length && - hasPermission(currentUser, 'CREATE_ROLES') && ( -

- {`${widgetsRole?.role?.label || 'Users'}'s widgets`} -

- )} - -
- {(isFetchingQuery || loading) && ( -
- {' '} - Loading widgets... -
- )} - - { rolesWidgets && - rolesWidgets.map((widget) => ( - - ))} -
- - {!!rolesWidgets.length &&
} - -
- - - {hasPermission(currentUser, 'READ_USERS') && -
-
-
-
- Users -
-
- {users} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ROLES') && -
-
-
-
- Roles -
-
- {roles} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PERMISSIONS') && -
-
-
-
- Permissions -
-
- {permissions} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ORGANIZATIONS') && -
-
-
-
- Organizations -
-
- {organizations} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_TRUST_PROFILES') && -
-
-
-
- Trust profiles -
-
- {trust_profiles} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_IDENTITY_VERIFICATIONS') && -
-
-
-
- Identity verifications -
-
- {identity_verifications} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_WALLETS') && -
-
-
-
- Wallets -
-
- {wallets} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_WALLET_TRANSACTIONS') && -
-
-
-
- Wallet transactions -
-
- {wallet_transactions} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_CHATS') && -
-
-
-
- Chats -
-
- {chats} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_CHAT_PARTICIPANTS') && -
-
-
-
- Chat participants -
-
- {chat_participants} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_MESSAGES') && -
-
-
-
- Messages -
-
- {messages} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_STORES') && -
-
-
-
- Stores -
-
- {stores} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ADDRESSES') && -
-
-
-
- Addresses -
-
- {addresses} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PRODUCT_CATEGORIES') && -
-
-
-
- Product categories -
-
- {product_categories} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PRODUCTS') && -
-
-
-
- Products -
-
- {products} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ORDERS') && -
-
-
-
- Orders -
-
- {orders} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_ORDER_ITEMS') && -
-
-
-
- Order items -
-
- {order_items} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PAYMENTS') && -
-
-
-
- Payments -
-
- {payments} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_PRODUCT_REVIEWS') && -
-
-
-
- Product reviews -
-
- {product_reviews} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_SHIPMENTS') && -
-
-
-
- Shipments -
-
- {shipments} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_POSTS') && -
-
-
-
- Posts -
-
- {posts} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_DEEP_DIVE_LINKS') && -
-
-
-
- Deep dive links -
-
- {deep_dive_links} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_CITATIONS') && -
-
-
-
- Citations -
-
- {citations} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_POST_ENGAGEMENTS') && -
-
-
-
- Post engagements -
-
- {post_engagements} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_REWARDS') && -
-
-
-
- Rewards -
-
- {rewards} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_FORUM_SPACES') && -
-
-
-
- Forum spaces -
-
- {forum_spaces} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_FORUM_THREADS') && -
-
-
-
- Forum threads -
-
- {forum_threads} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_FORUM_POSTS') && -
-
-
-
- Forum posts -
-
- {forum_posts} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_JOB_COMPANIES') && -
-
-
-
- Job companies -
-
- {job_companies} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_JOB_LISTINGS') && -
-
-
-
- Job listings -
-
- {job_listings} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_JOB_APPLICATIONS') && -
-
-
-
- Job applications -
-
- {job_applications} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_USER_CREDENTIALS') && -
-
-
-
- User credentials -
-
- {user_credentials} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_AFFILIATE_PROGRAMS') && -
-
-
-
- Affiliate programs -
-
- {affiliate_programs} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_AFFILIATE_MEMBERSHIPS') && -
-
-
-
- Affiliate memberships -
-
- {affiliate_memberships} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_COMMISSIONS') && -
-
-
-
- Commissions -
-
- {commissions} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_GEO_RULES') && -
-
-
-
- Geo rules -
-
- {geo_rules} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_FACTA_QUERIES') && -
-
-
-
- Facta queries -
-
- {facta_queries} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_FACTA_ANSWERS') && -
-
-
-
- Facta answers -
-
- {facta_answers} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_SUBSCRIPTIONS') && -
-
-
-
- Subscriptions -
-
- {subscriptions} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_API_CLIENTS') && -
-
-
-
- Api clients -
-
- {api_clients} -
-
-
- -
-
-
- } - - {hasPermission(currentUser, 'READ_AUDIT_EVENTS') && -
-
-
-
- Audit events -
-
- {audit_events} -
-
-
- -
-
-
- } - - -
-
- - ) -} - -Dashboard.getLayout = function getLayout(page: ReactElement) { - return {page} -} - -export default Dashboard +export default VortaCommercePage; diff --git a/frontend/src/pages/error.tsx b/frontend/src/pages/error.tsx index 4a6116e..51e09fe 100644 --- a/frontend/src/pages/error.tsx +++ b/frontend/src/pages/error.tsx @@ -17,7 +17,7 @@ export default function Error() { } + footer={} >

Unhandled exception

diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx index 1912a06..ff1ea24 100644 --- a/frontend/src/pages/index.tsx +++ b/frontend/src/pages/index.tsx @@ -1,166 +1,830 @@ - -import React, { useEffect, useState } from 'react'; -import type { ReactElement } from 'react'; +import React, { + FormEvent, + ReactElement, + useEffect, + useMemo, + useState, +} from 'react'; import Head from 'next/head'; import Link from 'next/link'; import BaseButton from '../components/BaseButton'; import CardBox from '../components/CardBox'; -import SectionFullScreen from '../components/SectionFullScreen'; import LayoutGuest from '../layouts/Guest'; -import BaseDivider from '../components/BaseDivider'; -import BaseButtons from '../components/BaseButtons'; import { getPageTitle } from '../config'; -import { useAppSelector } from '../stores/hooks'; -import CardBoxComponentTitle from "../components/CardBoxComponentTitle"; -import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'; +type TrustForm = { + profileName: string; + profileType: 'creator' | 'umkm' | 'company'; + verifiedDocuments: string; + hasBiometric: boolean; + hasTaxId: boolean; + hasTransactionHistory: boolean; + citedSources: string; + verifiedReviews: boolean; + localPresence: boolean; +}; -export default function Starter() { - const [illustrationImage, setIllustrationImage] = useState({ - src: undefined, - photographer: undefined, - photographer_url: undefined, - }) - const [illustrationVideo, setIllustrationVideo] = useState({video_files: []}) - const [contentType, setContentType] = useState('image'); - const [contentPosition, setContentPosition] = useState('background'); - const textColor = useAppSelector((state) => state.style.linkColor); +type TrustReport = TrustForm & { + id: string; + score: number; + tier: string; + createdAt: string; + recommendations: string[]; +}; - const title = 'Vorta Universe MVP' +const defaultForm: TrustForm = { + profileName: '', + profileType: 'umkm', + verifiedDocuments: '2', + hasBiometric: true, + hasTaxId: false, + hasTransactionHistory: true, + citedSources: '1', + verifiedReviews: false, + localPresence: true, +}; - // Fetch Pexels image/video - useEffect(() => { - async function fetchData() { - const image = await getPexelsImage(); - const video = await getPexelsVideo(); - setIllustrationImage(image); - setIllustrationVideo(video); - } - fetchData(); - }, []); +const storageKey = 'vorta-trust-reports'; - const imageBlock = (image) => ( - +const profileTypeLabels: Record = { + creator: 'Kreator', + umkm: 'UMKM', + company: 'Perusahaan', +}; + +const getTier = (score: number) => { + if (score >= 86) return 'Nexus Prime'; + if (score >= 70) return 'Pertumbuhan Terverifikasi'; + if (score >= 31) return 'Layak Pulse'; + + return 'Perlu Verifikasi'; +}; + +const clampNumber = (value: string, min: number, max: number) => { + const parsed = Number.parseInt(value, 10); + + if (Number.isNaN(parsed)) return min; + + return Math.min(Math.max(parsed, min), max); +}; + +const buildRecommendations = (form: TrustForm, score: number) => { + const recommendations = []; + const documents = clampNumber(form.verifiedDocuments, 0, 4); + const sources = clampNumber(form.citedSources, 0, 5); + + if (!form.hasBiometric) + recommendations.push( + 'Aktifkan pencocokan wajah untuk mengikat akun dengan manusia asli.', + ); + if (!form.hasTaxId) + recommendations.push( + 'Tambahkan NPWP atau legalitas bisnis untuk transaksi bernilai tinggi.', + ); + if (documents < 3) + recommendations.push( + 'Unggah minimal tiga dokumen valid agar skor Nexus naik lebih cepat.', + ); + if (sources < 3) + recommendations.push( + 'Tambahkan Deep Dive Link primer agar konten lolos cek sitasi Facta.', + ); + if (!form.verifiedReviews) + recommendations.push( + 'Kunci ulasan hanya dari pembeli terverifikasi untuk ulasan bebas manipulasi.', + ); + if (score < 31) + recommendations.push( + 'Prioritaskan verifikasi dasar agar dapat menulis di Vorta Pulse.', ); - const videoBlock = (video) => { - if (video?.video_files?.length > 0) { - return ( -
- - -
) - } + return recommendations.length + ? recommendations + : ['Profil siap diprioritaskan untuk visibilitas organik lintas VORTA.']; +}; + +const calculateReport = (form: TrustForm): TrustReport => { + const documents = clampNumber(form.verifiedDocuments, 0, 4); + const sources = clampNumber(form.citedSources, 0, 5); + const score = Math.min( + 100, + 14 + + documents * 9 + + (form.hasBiometric ? 18 : 0) + + (form.hasTaxId ? 12 : 0) + + (form.hasTransactionHistory ? 14 : 0) + + sources * 4 + + (form.verifiedReviews ? 10 : 0) + + (form.localPresence ? 6 : 0), + ); + + return { + ...form, + verifiedDocuments: String(documents), + citedSources: String(sources), + id: `${Date.now()}`, + score, + tier: getTier(score), + createdAt: new Date().toISOString(), + recommendations: buildRecommendations(form, score), + }; +}; + +const scoreGradient = (score: number) => { + if (score >= 70) return 'from-emerald-400 to-cyan-300'; + if (score >= 31) return 'from-amber-300 to-orange-400'; + + return 'from-rose-400 to-red-500'; +}; + +const scrollToSection = (sectionId: string) => { + const element = document.getElementById(sectionId); + + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } +}; + +export default function VortaLanding() { + const [form, setForm] = useState(defaultForm); + const [reports, setReports] = useState([]); + const [activeReportId, setActiveReportId] = useState(''); + const [errorMessage, setErrorMessage] = useState(''); + + useEffect(() => { + try { + const stored = window.localStorage.getItem(storageKey); + const parsedReports = stored ? (JSON.parse(stored) as TrustReport[]) : []; + + if (Array.isArray(parsedReports)) { + const normalizedReports = parsedReports.map((report) => ({ + ...report, + tier: getTier(report.score), + recommendations: buildRecommendations(report, report.score), + })); + + setReports(normalizedReports); + setActiveReportId(normalizedReports[0]?.id || ''); + } + } catch (error) { + console.error( + 'Failed to read VORTA trust reports from localStorage:', + error, + ); + window.localStorage.removeItem(storageKey); + } + }, []); + + useEffect(() => { + window.localStorage.setItem(storageKey, JSON.stringify(reports)); + }, [reports]); + + const activeReport = useMemo( + () => reports.find((report) => report.id === activeReportId) || reports[0], + [activeReportId, reports], + ); + + const updateField = (field: keyof TrustForm, value: string | boolean) => { + setForm((current) => ({ ...current, [field]: value })); + }; + + const saveReport = (nextForm: TrustForm) => { + const report = calculateReport(nextForm); + + setReports((current) => [report, ...current].slice(0, 6)); + setActiveReportId(report.id); + window.setTimeout(() => scrollToSection('reports'), 100); + }; + + const handleSubmit = (event: FormEvent) => { + event.preventDefault(); + const trimmedName = form.profileName.trim(); + + if (trimmedName.length < 3) { + setErrorMessage( + 'Nama profil minimal 3 karakter agar laporan dapat dibuat.', + ); + return; + } + + setErrorMessage(''); + saveReport({ ...form, profileName: trimmedName }); + }; + + const handleSampleReport = () => { + const sampleForm: TrustForm = { + profileName: 'Warung Nusantara Bandung', + profileType: 'umkm', + verifiedDocuments: '4', + hasBiometric: true, + hasTaxId: true, + hasTransactionHistory: true, + citedSources: '4', + verifiedReviews: true, + localPresence: true, }; + setForm(sampleForm); + setErrorMessage(''); + saveReport(sampleForm); + }; + + const handleResetForm = () => { + setForm(defaultForm); + setErrorMessage(''); + scrollToSection('trust-flow'); + }; + + const stats = [ + { label: 'kanal utama', value: '4 mode' }, + { label: 'ruang kerja', value: '1 app' }, + { label: 'akses ringan', value: '<3 detik' }, + ]; + + const pillars = [ + [ + 'Mega Super-App', + 'Chat bisnis, sosial, marketplace, dan dompet dalam satu ruang kerja ringan.', + ], + [ + 'Vorta Nexus', + 'Skor kepercayaan 0-100 untuk identitas, bisnis, ulasan, dan akses komunitas.', + ], + [ + 'Facta.AI', + 'Riset bebas SEO spam dengan Truth Score dan peta rujukan langsung.', + ], + [ + 'Vorta Synapse', + 'Protokol distribusi, afiliasi, dan logistik pintar untuk ekonomi kreator.', + ], + ]; + return ( -
+ <> - {getPageTitle('Starter Page')} + {getPageTitle('01 Mega Super-App')} + - -
- {contentType === 'image' && contentPosition !== 'background' - ? imageBlock(illustrationImage) - : null} - {contentType === 'video' && contentPosition !== 'background' - ? videoBlock(illustrationVideo) - : null} -
- - - -
-

This is a React.js/Node.js app generated by the Flatlogic Web App Generator

-

For guides and documentation please check - your local README.md and the Flatlogic documentation

+
+
+
+
+ + + +
+ {[ + ['pillars', 'Super-App'], + ['trust-flow', 'Skor'], + ['reports', 'Laporan'], + ].map(([sectionId, label]) => ( + + ))} +
+ +
+
+
+ Fokus pertama: 01 Mega Super-App +
+

+ Chat bisnis, sosial, marketplace, dan dompet dalam satu ruang + kerja ringan. +

+

+ Mulai dari percakapan tim, feed komunitas, etalase produk, + pembayaran, sampai riwayat transaksi—semuanya berjalan di satu + workspace cepat dengan identitas dan skor kepercayaan bawaan. +

+
scrollToSection('pillars')} + label='Lihat Alur Super-App' + color='success' + roundedFull + className='border-emerald-300 bg-[#1DE9B6] px-7 py-3 text-slate-950 hover:bg-white' /> + scrollToSection('trust-flow')} + label='Simulasikan Kepercayaan' + color='white' + outline + roundedFull + className='border-white/30 bg-white/5 px-7 py-3 text-white hover:bg-white hover:text-slate-950' + /> + +
+
+ {stats.map((stat) => ( +
+
+ {stat.value} +
+
+ {stat.label} +
+
+ ))} +
+
- - -
-
- -
-

© 2026 {title}. All rights reserved

- - Privacy Policy - -
+
+
+ +
+
+
+

+ Pratinjau ruang kerja langsung +

+

+ Mega Super-App Workspace +

+
+ + AKTIF + +
+
+
+
+ {[ + 'Chat bisnis', + 'Feed sosial', + 'Marketplace', + 'Dompet digital', + ].map((item) => ( +
+
+

{item}

+
+ ))} +
+
+
+
+

+ Aktivitas terpadu hari ini +

+

128

+
+

+ Transaksi & pesan sinkron +

+
+
+
+
+
+
+ +
+
+
-
+
+
+
+

+ Fokus pertama: 01 Mega Super-App +

+

+ Satu ruang kerja ringan yang menghubungkan percakapan, + komunitas, perdagangan, pembayaran, dan reputasi pengguna. +

+
+
+ {pillars.map(([title, description], index) => ( +
+
+ 0{index + 1} +
+

{title}

+

+ {description} +

+
+ ))} +
+
+
+ +
+
+
+

+ Mesin kepercayaan bawaan +

+

+ Skor Kepercayaan untuk Super-App +

+

+ Setiap akun, toko, percakapan, ulasan, dan transaksi bisa + diberi sinyal kepercayaan agar marketplace dan komunitas tetap + aman tanpa terasa berat bagi pengguna. +

+
+ Aturan validasi: nama + profil wajib minimal 3 karakter. Skor 31+ menandakan profil + layak masuk feed sosial, chat bisnis, dan marketplace publik. +
+
+ + +
+
+ + + + + + + +
+ +
+ {[ + ['hasBiometric', 'Pencocokan wajah biometrik aktif'], + ['hasTaxId', 'NPWP / legalitas bisnis valid'], + ['hasTransactionHistory', 'Riwayat transaksi bersih'], + ['verifiedReviews', 'Ulasan dikunci oleh bukti bayar'], + ['localPresence', 'Lokasi nyata / geofence aktif'], + ].map(([field, label]) => ( + + ))} +
+ + {errorMessage ? ( +
+ {errorMessage} +
+ ) : null} + +
+

+ Laporan tersimpan di browser ini sebagai riwayat demo MVP. +

+
+ + + +
+
+
+
+
+
+ +
+
+ +
+

+ Daftar Laporan Kepercayaan +

+

+ Pilih laporan untuk membuka detail simulasi. +

+
+
+ {reports.length ? ( + reports.map((report) => ( + + )) + ) : ( +
+

+ Belum ada laporan. Isi simulator di atas untuk membuat + Laporan Kepercayaan pertama. +

+ +
+ )} +
+
+ + + {activeReport ? ( +
+
+

+ Detail Laporan Kepercayaan +

+
+
+

+ {activeReport.profileName} +

+

+ {getTier(activeReport.score)} +

+
+
+ {activeReport.score} +
+
+
+
+
+
+ +
+
+

Ringkasan sinyal

+
+
+
Dokumen
+
+ {activeReport.verifiedDocuments}/4 +
+
+
+
Deep Dive Links
+
+ {activeReport.citedSources}/5 +
+
+
+
Akses Pulse
+
+ {activeReport.score >= 31 + ? 'Memenuhi syarat' + : 'Terkunci'} +
+
+
+
Integritas ulasan
+
+ {activeReport.verifiedReviews + ? 'Terlindungi' + : 'Terbuka'} +
+
+
+
+
+

Aksi terbaik berikutnya

+
    + {activeReport.recommendations.map((recommendation) => ( +
  • + + {recommendation} +
  • + ))} +
+
+
+
+ ) : ( +
+
+ 🛡️ +
+

+ Detail laporan akan muncul di sini +

+

+ Buat Laporan Kepercayaan pertama untuk melihat tier, + progress bar, dan rekomendasi. +

+
+ )} + +
+
+ +
+
+

+ © 2026 VORTA Universe MVP. Dimulai dari 01 Mega Super-App untuk + chat, sosial, marketplace, dan dompet ringan. +

+
+ + Kebijakan Privasi + + + Ketentuan Penggunaan + + + Panel Admin + +
+
+
+ + ); } -Starter.getLayout = function getLayout(page: ReactElement) { +VortaLanding.getLayout = function getLayout(page: ReactElement) { return {page}; }; - diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx index 78fe269..bfae6e2 100644 --- a/frontend/src/pages/login.tsx +++ b/frontend/src/pages/login.tsx @@ -44,7 +44,7 @@ export default function Login() { password: '9654d2ec', remember: true }) - const title = 'Vorta Universe MVP' + const title = 'VORTA-COMMERCE' // Fetch Pexels image/video useEffect( () => { @@ -65,7 +65,7 @@ export default function Login() { // Redirect to dashboard if user is logged in useEffect(() => { if (currentUser?.id) { - router.push('/dashboard'); + router.push('/vorta-commerce'); } }, [currentUser?.id, router]); // Show error message if there is one diff --git a/frontend/src/pages/mega-super-app.tsx b/frontend/src/pages/mega-super-app.tsx new file mode 100644 index 0000000..077579e --- /dev/null +++ b/frontend/src/pages/mega-super-app.tsx @@ -0,0 +1,660 @@ +import * as icon from '@mdi/js'; +import Head from 'next/head'; +import React, { ReactElement } from 'react'; +import BaseButton from '../components/BaseButton'; +import BaseIcon from '../components/BaseIcon'; +import CardBox from '../components/CardBox'; +import LayoutAuthenticated from '../layouts/Authenticated'; +import SectionMain from '../components/SectionMain'; +import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'; +import { getPageTitle } from '../config'; +import { useAppSelector } from '../stores/hooks'; + +type BusinessMessage = { + id: number; + sender: string; + text: string; + time: string; + mine?: boolean; +}; + +type SocialPost = { + id: number; + author: string; + text: string; + tag: string; + likes: number; +}; + +type Product = { + id: number; + name: string; + seller: string; + price: number; + badge: string; + stock: number; +}; + +type CartItem = Product & { + qty: number; +}; + +type WalletTransaction = { + id: number; + label: string; + amount: number; + type: 'in' | 'out'; + note: string; +}; + +type Activity = { + id: number; + module: string; + text: string; + time: string; +}; + +const fallbackIcon = icon.mdiViewDashboardOutline; +const resolveIcon = (name: string) => + (name in icon ? icon[name as keyof typeof icon] : fallbackIcon) as string; + +const formatCurrency = (value: number) => + new Intl.NumberFormat('id-ID', { + style: 'currency', + currency: 'IDR', + maximumFractionDigits: 0, + }).format(value); + +const MegaSuperAppPage = () => { + const { currentUser } = useAppSelector((state) => state.auth); + const iconsColor = useAppSelector((state) => state.style.iconsColor); + + const [activeConversation, setActiveConversation] = React.useState('Mitra Grosir Bandung'); + const [messageInput, setMessageInput] = React.useState(''); + const [postInput, setPostInput] = React.useState(''); + const [walletInput, setWalletInput] = React.useState('25000'); + const [walletNote, setWalletNote] = React.useState('Top up saldo operasional'); + const [balance, setBalance] = React.useState(1250000); + + const [messages, setMessages] = React.useState([ + { + id: 1, + sender: 'Mitra Grosir Bandung', + text: 'Stok paket reseller sudah siap. Mau langsung dibuatkan invoice marketplace?', + time: '09:12', + }, + { + id: 2, + sender: 'Anda', + text: 'Ya, kirim 12 paket. Pembayaran saya pakai dompet workspace.', + time: '09:15', + mine: true, + }, + { + id: 3, + sender: 'Mitra Grosir Bandung', + text: 'Siap. Saya tautkan order ke chat ini supaya tracking reputasi ikut naik.', + time: '09:17', + }, + ]); + + const [posts, setPosts] = React.useState([ + { + id: 1, + author: 'Komunitas Seller Lokal', + text: 'Promo bareng akhir pekan: bundling produk komunitas, cashback dompet, dan diskusi live di feed.', + tag: 'Kolaborasi', + likes: 42, + }, + { + id: 2, + author: 'Rina Kopi Nusantara', + text: 'Baru upload katalog kopi 250gr. Pembeli dari chat bisnis bisa checkout tanpa pindah aplikasi.', + tag: 'Marketplace', + likes: 27, + }, + ]); + + const products: Product[] = [ + { + id: 1, + name: 'Paket Reseller Kopi 12 pcs', + seller: 'Kopi Nusantara', + price: 360000, + badge: 'Terhubung chat', + stock: 18, + }, + { + id: 2, + name: 'Voucher Iklan Feed Komunitas', + seller: '01 Ads Lite', + price: 150000, + badge: 'Naikkan reputasi', + stock: 40, + }, + { + id: 3, + name: 'Jasa Desain Katalog UMKM', + seller: 'Studio Pasar Sosial', + price: 275000, + badge: 'Escrow dompet', + stock: 7, + }, + ]; + + const [cart, setCart] = React.useState([ + { ...products[0], qty: 1 }, + ]); + + const [transactions, setTransactions] = React.useState([ + { + id: 1, + label: 'Pembayaran order kopi', + amount: 360000, + type: 'out', + note: 'Marketplace • escrow aktif', + }, + { + id: 2, + label: 'Cashback komunitas seller', + amount: 50000, + type: 'in', + note: 'Reward sosial', + }, + { + id: 3, + label: 'Top up dompet', + amount: 500000, + type: 'in', + note: 'Bank transfer', + }, + ]); + + const [activities, setActivities] = React.useState([ + { + id: 1, + module: 'Chat', + text: 'Negosiasi dengan Mitra Grosir Bandung ditautkan ke order #MSA-1201.', + time: 'Baru saja', + }, + { + id: 2, + module: 'Marketplace', + text: '1 produk masuk keranjang dan siap dibayar dari Dompet 01.', + time: '3 menit lalu', + }, + { + id: 3, + module: 'Sosial', + text: 'Posting komunitas seller mendapat 42 suka dan 8 calon pembeli.', + time: '12 menit lalu', + }, + ]); + + const userName = currentUser?.firstName || currentUser?.email || 'Pengguna 01'; + const cartTotal = cart.reduce((sum, item) => sum + item.price * item.qty, 0); + const projectedBalance = balance - cartTotal; + + const addActivity = (module: string, text: string) => { + setActivities((current) => [ + { + id: Date.now(), + module, + text, + time: 'Baru saja', + }, + ...current.slice(0, 5), + ]); + }; + + const sendMessage = () => { + const trimmed = messageInput.trim(); + + if (!trimmed) { + return; + } + + setMessages((current) => [ + ...current, + { + id: Date.now(), + sender: 'Anda', + text: trimmed, + time: 'Sekarang', + mine: true, + }, + ]); + setMessageInput(''); + addActivity('Chat', `Pesan bisnis baru dikirim ke ${activeConversation}.`); + }; + + const publishPost = () => { + const trimmed = postInput.trim(); + + if (!trimmed) { + return; + } + + setPosts((current) => [ + { + id: Date.now(), + author: String(userName), + text: trimmed, + tag: 'Update Bisnis', + likes: 0, + }, + ...current, + ]); + setPostInput(''); + addActivity('Sosial', 'Update baru diterbitkan ke feed komunitas.'); + }; + + const addToCart = (product: Product) => { + setCart((current) => { + const existing = current.find((item) => item.id === product.id); + + if (existing) { + return current.map((item) => + item.id === product.id ? { ...item, qty: item.qty + 1 } : item, + ); + } + + return [...current, { ...product, qty: 1 }]; + }); + addActivity('Marketplace', `${product.name} ditambahkan ke keranjang terpadu.`); + }; + + const checkoutCart = () => { + if (!cart.length || cartTotal > balance) { + return; + } + + setBalance((current) => current - cartTotal); + setTransactions((current) => [ + { + id: Date.now(), + label: 'Checkout marketplace terpadu', + amount: cartTotal, + type: 'out', + note: `${cart.length} item • escrow dompet`, + }, + ...current, + ]); + setCart([]); + addActivity('Dompet', `Checkout ${formatCurrency(cartTotal)} berhasil memakai Dompet 01.`); + }; + + const addWalletTransaction = () => { + const amount = Number(walletInput); + const trimmedNote = walletNote.trim() || 'Top up dompet workspace'; + + if (!amount || amount <= 0) { + return; + } + + setBalance((current) => current + amount); + setTransactions((current) => [ + { + id: Date.now(), + label: 'Top up cepat', + amount, + type: 'in', + note: trimmedNote, + }, + ...current, + ]); + setWalletInput(''); + setWalletNote(''); + addActivity('Dompet', `Saldo bertambah ${formatCurrency(amount)} dari top up cepat.`); + }; + + const conversations = [ + { + name: 'Mitra Grosir Bandung', + status: 'Invoice marketplace siap', + unread: 2, + }, + { + name: 'Komunitas Seller Lokal', + status: 'Diskusi promo bareng', + unread: 4, + }, + { + name: 'Customer Prioritas', + status: 'Butuh update pengiriman', + unread: 1, + }, + ]; + + return ( + <> + + {getPageTitle('Mega Super-App')} + + + + + + + +
+
+
+ + Workspace ringan untuk bisnis, sosial, marketplace, dan dompet +
+

+ Halo {userName}, kelola semua aktivitas dari satu ruang kerja. +

+

+ MVP ini menyatukan chat bisnis, feed komunitas, katalog marketplace, + keranjang, pembayaran dompet, dan aktivitas terpadu tanpa pindah halaman. +

+
+ {[ + { label: 'Chat aktif', value: conversations.length, iconName: 'mdiChatProcessingOutline' }, + { label: 'Posting sosial', value: posts.length, iconName: 'mdiPostOutline' }, + { label: 'Produk live', value: products.length, iconName: 'mdiStorefrontOutline' }, + { label: 'Saldo dompet', value: formatCurrency(balance), iconName: 'mdiWalletOutline' }, + ].map((item) => ( +
+ +
+ {item.label} +
+
{item.value}
+
+ ))} +
+
+
+
+
+

Status workspace

+

Siap operasional

+
+ + Live MVP + +
+
+ {activities.slice(0, 4).map((activity) => ( +
+
+ {activity.module} + {activity.time} +
+

{activity.text}

+
+ ))} +
+
+
+
+ +
+ +
+ + +
+
+

Ruang chat aktif

+

{activeConversation}

+
+
+ {messages.map((message) => ( +
+
+
+ {message.sender} • {message.time} +
+

{message.text}

+
+
+ ))} +
+
+
+ setMessageInput(event.target.value)} + onKeyDown={(event) => { + if (event.key === 'Enter') { + sendMessage(); + } + }} + className="h-11 flex-1 rounded-xl border border-gray-200 bg-white px-3 text-sm outline-none focus:border-blue-400 focus:ring dark:border-dark-700 dark:bg-dark-900" + placeholder="Tulis pesan, buat invoice, atau tautkan produk..." + /> + +
+
+
+
+
+ +
+ +
+
+
+

Dompet 01

+

Saldo, top up, checkout, dan escrow.

+
+ +
+
+

Saldo tersedia

+
{formatCurrency(balance)}
+

Estimasi setelah checkout: {formatCurrency(projectedBalance)}

+
+
+ setWalletInput(event.target.value)} + type="number" + min="0" + className="h-10 rounded-xl border border-gray-200 bg-white px-3 text-sm outline-none focus:border-blue-400 focus:ring dark:border-dark-700 dark:bg-dark-900" + placeholder="Nominal top up" + /> + setWalletNote(event.target.value)} + className="h-10 rounded-xl border border-gray-200 bg-white px-3 text-sm outline-none focus:border-blue-400 focus:ring dark:border-dark-700 dark:bg-dark-900" + placeholder="Catatan" + /> +
+ +
+ {transactions.slice(0, 4).map((transaction) => ( +
+
+

{transaction.label}

+

{transaction.note}

+
+ + {transaction.type === 'in' ? '+' : '-'}{formatCurrency(transaction.amount)} + +
+ ))} +
+
+
+
+
+ +
+ +
+
+
+

Feed Sosial Bisnis

+

Bangun komunitas dan reputasi sambil menjual.

+
+ +
+