From 6a68d109cf0d0c2b5ea27e04acdbff0fcb30f7d9 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Thu, 7 Sep 2023 10:47:33 +0200 Subject: [PATCH 01/33] feat(i18n): add Turkish locale --- src/app/api/i18n/locales/tr.ts | 89 ++++++++++++++++++++++++++++++ src/components/language-choice.tsx | 1 + src/components/providers.tsx | 2 +- 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/app/api/i18n/locales/tr.ts diff --git a/src/app/api/i18n/locales/tr.ts b/src/app/api/i18n/locales/tr.ts new file mode 100644 index 0000000..1f22437 --- /dev/null +++ b/src/app/api/i18n/locales/tr.ts @@ -0,0 +1,89 @@ +import { Dictionary } from '@/app/api/i18n/schema' + +const tr: Dictionary = { + common: { + title: 'Mina Protocol Evreni', + dashboard: 'Gösterge ‘Paneli', + accounts: 'Hesaplar', + transactions: 'İşlemler', + staking: 'Staking', + settings: 'Ayarlar', + trulyOpen: 'Gerçek anlamda açık Mina Explorer', + createdAndMaintained: + "Palladian'lar tarafından geliştirildi ve sürdürülüyor.", + needWallet: + 'Bir Mina cüzdanına mı ihtiyaç duyuyorsun? Pallad \uD83E\uDD8B yakında geliyor.', + privacyPolicy: 'Gizlilik Politikası', + termsAndConditions: 'Şartlar ve Koşullar', + serviceStatus: 'Servis Durumu', + typeCommand: 'Bir komut yazın veya arama yapın...', + noResults: 'Sonuç bulunamadı.', + publicKey: 'Açık Anahtar', + balance: 'Bakiye', + nonce: 'Nonce', + delegate: 'Delege', + somethingWentWrong: 'Bir şeyler ters gitti!', + tryAgain: 'Tekrar deneyin', + hash: 'Hash', + from: 'Gönderici', + to: 'Alıcı', + amount: 'Miktar', + date: 'Tarih', + dateTime: 'Tarih', + mina: 'MINA', + columns: 'Sütunlar', + language: 'Dil', + environments: 'Ağlar', + network: 'Ağ', + theme: 'Tema', + fiatCurrency: 'Fiat Para Birimi', + comingSoon: 'Yakında', + version: 'Versiyon', + about: 'Hakkında', + toggleTheme: 'Toggle Tema', + light: 'Aydınlık', + dark: 'Karanlık', + system: 'Sistem', + seeAll: 'Hepsini Gör', + close: 'Kapat', + minaAmount: '{amount} MINA', + username: 'Kullanıcı Adı', + searchWithPublicKey: 'Açık anahtar ile ara', + searchWithHash: 'Hash ile ara', + name: 'Ad', + delegates: 'Delegeler', + blockChance: 'Şans', + percentOfStake: 'Stake yüzdesi', + stake: 'Stake', + kind: 'Tür', + valueCopied: 'Değer panoya kopyalandı.', + languages: 'Diller', + actions: 'Eylemler', + incoming: 'Gelen', + outgoing: 'Giden' + }, + accounts: { + accountOverview: 'Hesaba Genel Bakış', + accountsCount: 'Hesaplar ({count})' + }, + dashboard: { + header: 'Hızlı İstatistikler', + epoch: 'Epoch', + slot: 'Slot', + circulatingSupply: 'Dolaşımdaki Arz', + totalCurrency: 'Toplam Arz', + minaPrice: 'Mina Fiyatı', + minaMarketCap: 'Mina Piyasa Değeri', + outOfSlots: '7140 dışında' + }, + staking: { + stakingCount: 'Staking ({count})' + }, + transactions: { + transactionDetails: 'İşlem Detayı', + latestTransactions: 'Son işlemler', + transactionsCount: 'İşlemler ({count})' + } +} as const + +export default tr diff --git a/src/components/language-choice.tsx b/src/components/language-choice.tsx index 70d8cd1..b0ac716 100644 --- a/src/components/language-choice.tsx +++ b/src/components/language-choice.tsx @@ -26,6 +26,7 @@ const useSetLanguage = () => { const LANGUAGES = [ { label: 'English', value: 'en' }, + { label: 'Türkçe', value: 'tr' }, { label: 'Polski', value: 'pl' } ] diff --git a/src/components/providers.tsx b/src/components/providers.tsx index 666a9c5..6149ed7 100644 --- a/src/components/providers.tsx +++ b/src/components/providers.tsx @@ -52,7 +52,7 @@ const Providers = ({ useEffect(() => { const handleLanguageParam = async () => { if (!nextLanguage) return - const correctLang = ['en', 'pl'].includes(nextLanguage) + const correctLang = ['en', 'pl', 'tr'].includes(nextLanguage) if (!correctLang) return await setLocale(nextLanguage) router.refresh() From 8f8d90b2a8fbff0317605009c04ededfa8f698ee Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Thu, 7 Sep 2023 22:49:41 +0200 Subject: [PATCH 02/33] feat(i18n): update Turkish locale --- .eslintrc.json | 3 ++- src/app/api/i18n/locales/en.ts | 3 ++- src/app/api/i18n/locales/pl.ts | 3 ++- src/app/api/i18n/locales/tr.ts | 5 +++-- src/app/api/i18n/schema.ts | 3 ++- src/app/page.tsx | 8 ++++++-- src/components/accounts/accounts-table.tsx | 8 +++++++- src/lib/currency.test.ts | 12 ++++++++++++ src/lib/number.test.ts | 13 +++++++++++++ src/lib/string.test.ts | 8 ++++++++ 10 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 src/lib/currency.test.ts create mode 100644 src/lib/number.test.ts create mode 100644 src/lib/string.test.ts diff --git a/.eslintrc.json b/.eslintrc.json index 2fab55d..e03d6a6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -8,6 +8,7 @@ "simple-import-sort/exports": "error", "@typescript-eslint/no-unused-vars": "error", "import/no-extraneous-dependencies": "error", - "react/jsx-no-literals": [2, {"noStrings": true, "ignoreProps": true}] + "react/jsx-no-literals": [2, {"noStrings": true, "ignoreProps": true}], + "no-console": "error" } } diff --git a/src/app/api/i18n/locales/en.ts b/src/app/api/i18n/locales/en.ts index 2a8f9ea..d835165 100644 --- a/src/app/api/i18n/locales/en.ts +++ b/src/app/api/i18n/locales/en.ts @@ -58,7 +58,8 @@ const en: Dictionary = { languages: 'Languages', actions: 'Actions', incoming: 'Incoming', - outgoing: 'Outgoing' + outgoing: 'Outgoing', + unknown: 'Unknown' }, accounts: { accountOverview: 'Account Overview', diff --git a/src/app/api/i18n/locales/pl.ts b/src/app/api/i18n/locales/pl.ts index 3cd38db..f7d706e 100644 --- a/src/app/api/i18n/locales/pl.ts +++ b/src/app/api/i18n/locales/pl.ts @@ -58,7 +58,8 @@ const pl: Dictionary = { languages: 'Języki', actions: 'Akcje', incoming: 'Przychodzące', - outgoing: 'Wychodzące' + outgoing: 'Wychodzące', + unknown: 'Nieznana' }, accounts: { accountOverview: 'Przegląd konta', diff --git a/src/app/api/i18n/locales/tr.ts b/src/app/api/i18n/locales/tr.ts index 1f22437..d04809a 100644 --- a/src/app/api/i18n/locales/tr.ts +++ b/src/app/api/i18n/locales/tr.ts @@ -8,7 +8,7 @@ const tr: Dictionary = { transactions: 'İşlemler', staking: 'Staking', settings: 'Ayarlar', - trulyOpen: 'Gerçek anlamda açık Mina Explorer', + trulyOpen: 'Gerçek anlamda açık Mina Explorer.', createdAndMaintained: "Palladian'lar tarafından geliştirildi ve sürdürülüyor.", needWallet: @@ -60,7 +60,8 @@ const tr: Dictionary = { languages: 'Diller', actions: 'Eylemler', incoming: 'Gelen', - outgoing: 'Giden' + outgoing: 'Giden', + unknown: 'Bilinmeyen' }, accounts: { accountOverview: 'Hesaba Genel Bakış', diff --git a/src/app/api/i18n/schema.ts b/src/app/api/i18n/schema.ts index 5a58216..42a3e85 100644 --- a/src/app/api/i18n/schema.ts +++ b/src/app/api/i18n/schema.ts @@ -58,7 +58,8 @@ export const localeSchema = z.object({ languages: z.string(), actions: z.string(), incoming: z.string(), - outgoing: z.string() + outgoing: z.string(), + unknown: z.string() }), accounts: z.object({ accountOverview: z.string(), diff --git a/src/app/page.tsx b/src/app/page.tsx index d264265..37e2b86 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -5,9 +5,13 @@ import { QuickStats } from '@/components/dashboard/quick-stats' import { SummaryOverview } from '@/components/dashboard/summary-overview' import { fetchCoinData } from '@/data/coin' import { getLocale, getT } from '@/lib/i18n/server' +import { titleTemplate } from '@/lib/metadata' -export const metadata: Metadata = { - title: 'The universe of Mina Protocol - Minaverse' +export const generateMetadata = async (): Promise => { + const t = await getT() + return { + title: titleTemplate(t('common.title')) + } } const HomePage = async () => { diff --git a/src/components/accounts/accounts-table.tsx b/src/components/accounts/accounts-table.tsx index 20dffd8..9f8a0fe 100644 --- a/src/components/accounts/accounts-table.tsx +++ b/src/components/accounts/accounts-table.tsx @@ -123,7 +123,13 @@ export const AccountsTable = ({ { accessorKey: 'username', header: t('common.username'), - cell: ({ row }) =>
{row.getValue('username')}
+ cell: ({ row }) => { + const username = + row.getValue('username') === 'Unknown' + ? t('common.unknown') + : String(row.getValue('username')) + return
{username}
+ } }, { accessorKey: 'nonce', diff --git a/src/lib/currency.test.ts b/src/lib/currency.test.ts new file mode 100644 index 0000000..87ccd3b --- /dev/null +++ b/src/lib/currency.test.ts @@ -0,0 +1,12 @@ +import { formatCurrency } from '@/lib/currency' + +it('formats currency', () => { + const number = 235653 + expect( + formatCurrency({ + value: number, + locale: 'en', + currency: 'usd' + }) + ).toEqual('$235,653.00') +}) diff --git a/src/lib/number.test.ts b/src/lib/number.test.ts new file mode 100644 index 0000000..3e107bb --- /dev/null +++ b/src/lib/number.test.ts @@ -0,0 +1,13 @@ +import { formatNumber } from '@/lib/number' + +it('formats a number', () => { + const number = 235653 + expect(formatNumber({ value: number, locale: 'en' })).toEqual('235,653') +}) + +it('formats number as compact', () => { + const number = 235653 + expect(formatNumber({ value: number, locale: 'en', compact: true })).toEqual( + '236K' + ) +}) diff --git a/src/lib/string.test.ts b/src/lib/string.test.ts new file mode 100644 index 0000000..cbecad7 --- /dev/null +++ b/src/lib/string.test.ts @@ -0,0 +1,8 @@ +import { truncateString } from '@/lib/string' + +it('truncates a wallet address', () => { + const address = '0x1234567890abcdef1234567890abcdef12345678' + expect( + truncateString({ value: address, firstCharCount: 8, endCharCount: 8 }) + ).toEqual('0x123456...12345678') +}) From 815ecf1dd2ab739405e78f2773536cb6d64e3629 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Fri, 8 Sep 2023 09:11:21 +0200 Subject: [PATCH 03/33] feat(i18n): update Turkish locale --- src/app/api/i18n/locales/en.ts | 3 ++- src/app/api/i18n/locales/pl.ts | 3 ++- src/app/api/i18n/locales/tr.ts | 15 ++++++++------- src/app/api/i18n/schema.ts | 3 ++- src/components/mobile-navigation.tsx | 10 ++++++---- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/app/api/i18n/locales/en.ts b/src/app/api/i18n/locales/en.ts index d835165..b672f3f 100644 --- a/src/app/api/i18n/locales/en.ts +++ b/src/app/api/i18n/locales/en.ts @@ -59,7 +59,8 @@ const en: Dictionary = { actions: 'Actions', incoming: 'Incoming', outgoing: 'Outgoing', - unknown: 'Unknown' + unknown: 'Unknown', + menu: 'Menu' }, accounts: { accountOverview: 'Account Overview', diff --git a/src/app/api/i18n/locales/pl.ts b/src/app/api/i18n/locales/pl.ts index f7d706e..10a0638 100644 --- a/src/app/api/i18n/locales/pl.ts +++ b/src/app/api/i18n/locales/pl.ts @@ -59,7 +59,8 @@ const pl: Dictionary = { actions: 'Akcje', incoming: 'Przychodzące', outgoing: 'Wychodzące', - unknown: 'Nieznana' + unknown: 'Nieznana', + menu: 'Menu' }, accounts: { accountOverview: 'Przegląd konta', diff --git a/src/app/api/i18n/locales/tr.ts b/src/app/api/i18n/locales/tr.ts index d04809a..a21e15f 100644 --- a/src/app/api/i18n/locales/tr.ts +++ b/src/app/api/i18n/locales/tr.ts @@ -8,9 +8,9 @@ const tr: Dictionary = { transactions: 'İşlemler', staking: 'Staking', settings: 'Ayarlar', - trulyOpen: 'Gerçek anlamda açık Mina Explorer.', + trulyOpen: 'Gerçek anlamda açık Mina Exploreri.', createdAndMaintained: - "Palladian'lar tarafından geliştirildi ve sürdürülüyor.", + 'Palladian‘lar tarafından geliştirildi ve sürdürülüyor.', needWallet: 'Bir Mina cüzdanına mı ihtiyaç duyuyorsun? Pallad \uD83E\uDD8B yakında geliyor.', privacyPolicy: 'Gizlilik Politikası', @@ -18,10 +18,10 @@ const tr: Dictionary = { serviceStatus: 'Servis Durumu', typeCommand: 'Bir komut yazın veya arama yapın...', noResults: 'Sonuç bulunamadı.', - publicKey: 'Açık Anahtar', + publicKey: 'Public Key', balance: 'Bakiye', nonce: 'Nonce', - delegate: 'Delege', + delegate: 'Validatör', somethingWentWrong: 'Bir şeyler ters gitti!', tryAgain: 'Tekrar deneyin', hash: 'Hash', @@ -48,10 +48,10 @@ const tr: Dictionary = { close: 'Kapat', minaAmount: '{amount} MINA', username: 'Kullanıcı Adı', - searchWithPublicKey: 'Açık anahtar ile ara', + searchWithPublicKey: 'Public Key ile ara', searchWithHash: 'Hash ile ara', name: 'Ad', - delegates: 'Delegeler', + delegates: 'Delegatörler', blockChance: 'Şans', percentOfStake: 'Stake yüzdesi', stake: 'Stake', @@ -61,7 +61,8 @@ const tr: Dictionary = { actions: 'Eylemler', incoming: 'Gelen', outgoing: 'Giden', - unknown: 'Bilinmeyen' + unknown: 'Bilinmeyen', + menu: 'Menü' }, accounts: { accountOverview: 'Hesaba Genel Bakış', diff --git a/src/app/api/i18n/schema.ts b/src/app/api/i18n/schema.ts index 42a3e85..c41aa8e 100644 --- a/src/app/api/i18n/schema.ts +++ b/src/app/api/i18n/schema.ts @@ -59,7 +59,8 @@ export const localeSchema = z.object({ actions: z.string(), incoming: z.string(), outgoing: z.string(), - unknown: z.string() + unknown: z.string(), + menu: z.string() }), accounts: z.object({ accountOverview: z.string(), diff --git a/src/components/mobile-navigation.tsx b/src/components/mobile-navigation.tsx index b7409b8..0dd4c34 100644 --- a/src/components/mobile-navigation.tsx +++ b/src/components/mobile-navigation.tsx @@ -9,35 +9,37 @@ import { import { usePathname, useRouter } from 'next/navigation' import { Button } from '@/components/ui/button' +import { useTranslation } from '@/lib/i18n/client' import { cn } from '@/lib/utils' import { useAppStore } from '@/store/app' export const MobileNavigation = () => { + const { t } = useTranslation() const router = useRouter() const pathname = usePathname() const setCommandsOpen = useAppStore((state) => state.setCommandsOpen) const network = useAppStore((state) => state.network) const MENU_ITEMS = [ { - label: 'Dashboard', + label: t('common.dashboard'), url: '/', icon: LayoutDashboardIcon, onClick: () => router.push('/') }, { - label: 'Transactions', + label: t('common.transactions'), url: `/${network}/transactions`, icon: ArrowLeftRightIcon, onClick: () => router.push(`/${network}/transactions`) }, { - label: 'Staking', + label: t('common.staking'), url: `/${network}/staking`, icon: CoinsIcon, onClick: () => router.push(`/${network}/staking`) }, { - label: 'Menu', + label: t('common.menu'), icon: MenuIcon, onClick: () => setCommandsOpen(true) } From 157dd89f8f5edc28c416f480388ced4d7cfb842d Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Tue, 12 Sep 2023 22:08:53 +0200 Subject: [PATCH 04/33] feat(sitemap): add simple sitemap --- public/robots.txt | 9 ++++++ src/app/sitemap.ts | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 public/robots.txt create mode 100644 src/app/sitemap.ts diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..deb9e34 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,9 @@ +# * +User-agent: * +Allow: / + +# Host +Host: https://minaverse.xyz + +# Sitemaps +Sitemap: https://minaverse.xyz/sitemap.xml diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..680a0e9 --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,73 @@ +import { MetadataRoute } from 'next' + +import { fetchAccounts } from '@/data/accounts' +import { Network } from '@/data/api' +import { fetchStaking } from '@/data/staking' +import { env } from '@/env.mjs' + +const APP_URL = env.NEXT_PUBLIC_APP_URL + +const networks = [Network.MAINNET, Network.DEVNET, Network.BERKELEY] + +const getAccountsRoutes = ({ + network, + accountIds +}: { + network: Network + accountIds: string[] +}) => + accountIds.map((id) => ({ + url: `${APP_URL}/${network}/accounts/${id}`, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 0.8 + })) + +const getNetworkRoutes = async (network: Network) => { + const { data: stakePools } = await fetchStaking({ search: null, network }) + const { data: accounts } = await fetchAccounts({ search: null, network }) + return [ + { + url: `${APP_URL}/${network}/accounts`, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 0.8 + }, + ...getAccountsRoutes({ + accountIds: accounts.map(({ public_key }) => public_key), + network + }), + { + url: `${APP_URL}/${network}/transactions`, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 0.8 + }, + { + url: `${APP_URL}/${network}/staking`, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 0.8 + }, + ...getAccountsRoutes({ + accountIds: stakePools.map(({ _id }) => _id.delegate), + network + }) + ] +} + +export default async function sitemap(): Promise { + const routes = (await Promise.all( + networks.map(async (network) => await getNetworkRoutes(network)) + )) as MetadataRoute.Sitemap[] + + return [ + { + url: APP_URL, + lastModified: new Date(), + changeFrequency: 'weekly', + priority: 1 + }, + ...routes.flat() + ] +} From 0c54191898b1a507b599ae58cb16917392a50c38 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Tue, 12 Sep 2023 22:23:59 +0200 Subject: [PATCH 05/33] feat(sitemap): change runtime --- src/app/sitemap.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 680a0e9..2fc1dbc 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -71,3 +71,5 @@ export default async function sitemap(): Promise { ...routes.flat() ] } + +export const runtime = 'edge' From 5f7a699bc5a8b67966b0f2ffa91c515ffba1e787 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Wed, 13 Sep 2023 06:43:16 +0200 Subject: [PATCH 06/33] chore(repo): trigger deploy From 25a1ddab6eda0cf652c8c3343ac7bfb414fd7d33 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Wed, 13 Sep 2023 07:02:10 +0200 Subject: [PATCH 07/33] chore(repo): add meta keywords --- src/app/layout.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 2a89f5a..b2ee68e 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -24,6 +24,8 @@ export const metadata: Metadata = { title: 'Minaverse', description: 'The truly open Mina Explorer. Get insight into the Mina Protocol.', + keywords: + 'Mina, Blockchain, Explorer, Mina Protocol, Mina Price, Price of Mina, Mina Explorer, Market Cap, Mina Market Cap, zkApps, zkProofs, Succinct Blockchain', manifest: '/manifest.json', themeColor: '#1e3a8a' } From 691a77aabf02762b161f58c4027394cc2446bc1b Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Thu, 14 Sep 2023 09:27:20 +0200 Subject: [PATCH 08/33] feat(blog): refactor footer --- src/app/layout.tsx | 8 ++-- src/components/footer.tsx | 75 ++++++++++++++++++++++---------- src/components/service-links.tsx | 2 +- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index b2ee68e..363b0fa 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -59,7 +59,7 @@ export default function RootLayout({ -
+
}> @@ -76,18 +76,16 @@ export default function RootLayout({ }> -
{children}
+
{children}
}>
}> - }> - -
+ ) diff --git a/src/components/footer.tsx b/src/components/footer.tsx index 8521cfb..f37f40b 100644 --- a/src/components/footer.tsx +++ b/src/components/footer.tsx @@ -1,43 +1,72 @@ +/* eslint-disable react/jsx-no-literals */ import NextImage from 'next/image' import { FooterOptions } from '@/components/footer-options' import { ServiceLinks } from '@/components/service-links' +import { Button } from '@/components/ui/button' import { getT } from '@/lib/i18n/server' export const Footer = async () => { + const year = new Date().getFullYear() const t = await getT() return ( -
-
- -

{t('common.trulyOpen')}

+ ) } diff --git a/src/components/service-links.tsx b/src/components/service-links.tsx index ccff9d4..98de953 100644 --- a/src/components/service-links.tsx +++ b/src/components/service-links.tsx @@ -6,7 +6,7 @@ import { useTranslation } from '@/lib/i18n/client' export const ServiceLinks = () => { const { t } = useTranslation() return ( -
+

Edu

- - - - - + + Coming Soon +
diff --git a/src/components/navbar-addons.tsx b/src/components/navbar-addons.tsx new file mode 100644 index 0000000..95adb07 --- /dev/null +++ b/src/components/navbar-addons.tsx @@ -0,0 +1,19 @@ +'use client' + +import NextLink from 'next/link' +import { usePathname } from 'next/navigation' + +import { Button } from '@/components/ui/button' +import { useTranslation } from '@/lib/i18n/client' + +export const NavbarAddons = () => { + const pathname = usePathname() + const { t } = useTranslation() + return ( +
+ +
+ ) +} diff --git a/src/components/navbar-links.tsx b/src/components/navbar-links.tsx index 9b47c44..ec2bc40 100644 --- a/src/components/navbar-links.tsx +++ b/src/components/navbar-links.tsx @@ -30,7 +30,7 @@ export const NavbarLinks = ({ network = 'mainnet' }: { network: string }) => { ] return (
- + {NAV_LINKS.map((link) => (
) diff --git a/src/components/commands.tsx b/src/components/commands.tsx index d281fe9..85d8709 100644 --- a/src/components/commands.tsx +++ b/src/components/commands.tsx @@ -59,6 +59,11 @@ export const Commands = () => { onSelect: () => router.push('/staking'), testId: 'commands__staking' }, + { + label: t('common.blog'), + onSelect: () => router.push('/blog'), + testId: 'commands__blog' + }, { label: t('common.settings'), onSelect: () => setSettingsOpen(true), diff --git a/src/components/navbar-addons.tsx b/src/components/navbar-addons.tsx index 95adb07..82c9c43 100644 --- a/src/components/navbar-addons.tsx +++ b/src/components/navbar-addons.tsx @@ -11,7 +11,11 @@ export const NavbarAddons = () => { const { t } = useTranslation() return (
-
From 754f0dd8d2d491bb4b0087f355777326d09e0f32 Mon Sep 17 00:00:00 2001 From: Tomek Marciniak Date: Wed, 20 Sep 2023 16:34:16 +0200 Subject: [PATCH 16/33] fix(lint): format --- src/components/commands-button.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/commands-button.tsx b/src/components/commands-button.tsx index c9b19cb..c466785 100644 --- a/src/components/commands-button.tsx +++ b/src/components/commands-button.tsx @@ -1,9 +1,9 @@ 'use client' -import { CommandIcon, AlignRightIcon } from 'lucide-react' +import { AlignRightIcon, CommandIcon } from 'lucide-react' -import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' import { useAppStore } from '@/store/app' const K_KEY = 'K' From 5f7864274d567b3105aee74dd6ab22f00793ee8b Mon Sep 17 00:00:00 2001 From: Rafal Golawski Date: Fri, 22 Sep 2023 12:46:14 +0200 Subject: [PATCH 17/33] feat(accounts): add reporting feature, toast and dialog --- .env.example | 1 + package.json | 1 + pnpm-lock.yaml | 7 ++ .../[network]/accounts/[publicKey]/page.tsx | 17 ++- src/app/api/i18n/locales/en.ts | 14 ++- src/app/api/i18n/locales/pl.ts | 14 ++- src/app/api/i18n/locales/tr.ts | 14 ++- src/app/api/i18n/locales/uk.ts | 14 ++- src/app/api/i18n/schema.ts | 13 ++- .../accounts/account-report-dialog.tsx | 103 ++++++++++++++++++ src/components/accounts/account-sheet.tsx | 17 ++- .../accounts/account-suspicious-alert.tsx | 21 ++++ src/data/hooks.tsx | 11 ++ src/data/pocketbase.ts | 10 ++ src/data/restrictions.ts | 26 +++++ src/env.mjs | 6 +- 16 files changed, 280 insertions(+), 9 deletions(-) create mode 100644 src/components/accounts/account-report-dialog.tsx create mode 100644 src/components/accounts/account-suspicious-alert.tsx create mode 100644 src/data/pocketbase.ts create mode 100644 src/data/restrictions.ts diff --git a/.env.example b/.env.example index 343989b..bf1d2f4 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000 NEXT_PUBLIC_ERROR_REPORTING_URL= NEXT_PUBLIC_ERROR_REPORTING_APP_ID= +NEXT_PUBLIC_POCKETBASE_URL= diff --git a/package.json b/package.json index b1e1395..ca02413 100644 --- a/package.json +++ b/package.json @@ -68,6 +68,7 @@ "lucide-react": "^0.268.0", "next": "13.4.18", "next-themes": "^0.2.1", + "pocketbase": "^0.18.0", "postcss": "8.4.28", "pupa": "^3.1.0", "rambda": "^8.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 03af066..e0bf5b3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,6 +152,9 @@ dependencies: next-themes: specifier: ^0.2.1 version: 0.2.1(next@13.4.18)(react-dom@18.2.0)(react@18.2.0) + pocketbase: + specifier: ^0.18.0 + version: 0.18.0 postcss: specifier: 8.4.28 version: 8.4.28 @@ -4862,6 +4865,10 @@ packages: hasBin: true dev: true + /pocketbase@0.18.0: + resolution: {integrity: sha512-09ri0Rnm4JjboU4OJeibd6pgvKi4DPg/r/Uu/QI3mKSZsrROoMT75zyiOldbBBMWZUDG1TRlv6BjQj30SFsrVw==} + dev: false + /postcss-import@15.1.0(postcss@8.4.28): resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} diff --git a/src/app/[network]/accounts/[publicKey]/page.tsx b/src/app/[network]/accounts/[publicKey]/page.tsx index 7b63984..4156fd6 100644 --- a/src/app/[network]/accounts/[publicKey]/page.tsx +++ b/src/app/[network]/accounts/[publicKey]/page.tsx @@ -1,9 +1,12 @@ import { Metadata } from 'next' import { AccountDetails } from '@/components/accounts/account-details' +import { AccountReportDialog } from '@/components/accounts/account-report-dialog' +import { AccountSuspiciousAlert } from '@/components/accounts/account-suspicious-alert' import { AccountTransactions } from '@/components/accounts/account-transactions' import { fetchAccount } from '@/data/accounts' import { Network } from '@/data/api' +import { fetchRestrictions } from '@/data/restrictions' import { getLocale, getT } from '@/lib/i18n/server' export const generateMetadata = async ({ @@ -27,9 +30,21 @@ const AccountPage = async ({ publicKey: params.publicKey, network: params.network as Network }) + const restrictions = await fetchRestrictions({ + publicKey: params.publicKey + }) return (
-

{t('accounts.accountOverview')}

+
+

{t('accounts.accountOverview')}

+ +
+ {restrictions.items.length > 0 && ( + + )} { + const [open, setOpen] = useState(false) + const { t } = useTranslation() + const { toast } = useToast() + const handleSubmit = async (event: FormEvent) => { + event.preventDefault() + const formData = new FormData(event.currentTarget) + const validation = FormSchema.safeParse(Object.fromEntries(formData)) + if (!validation.success) return // invalid form + const { reason, evidence } = validation.data + const reportBody: ReportBody = { publicKey, description: reason } + if (evidence.size) { + if (evidence.size > Megabyte) return // invalid file size + if (!AllowedFileTypes.includes(evidence.type)) return // invalid file type + reportBody.attachment = evidence + } + await pb + .collection(Collections.reports) + .create(reportBody) + .then(() => { + setOpen(false) + toast({ description: t('accountReport.toast') }) + }) + } + return ( + + + + + + + {t('accountReport.title')} + +
+
+ +