Migrate front end to Mantine

This commit is contained in:
2023-09-22 16:44:30 -04:00
parent ab4073f280
commit 02a4d840e0
17 changed files with 102 additions and 113 deletions

View File

@@ -1,8 +1,8 @@
{
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120
"trailingComma": "none",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"jsxSingleQuote": true,
"printWidth": 120
}

View File

@@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"@ant-design/cssinjs": "^1.17.0",
"@blueprintjs/core": "^5.3.0",
"antd": "^5.9.0",
"@mantine/core": "^7.0.0",
"@mantine/hooks": "^7.0.0",
"@mantine/modals": "^7.0.0",
"axios": "^1.4.0",
"leaflet": "^1.9.4",
"next": "^13.4.19",
@@ -34,8 +34,9 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"postcss": "^8.4.28",
"postcss-import": "^15.1.0",
"postcss-preset-mantine": "^1.7.0",
"prettier": "^3.0.0",
"tailwindcss": "^3.3.3",
"typescript": "5.1.6"
}
}

View File

@@ -1,8 +1,7 @@
module.exports = {
plugins: {
'postcss-preset-mantine': {},
'postcss-import': {},
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
}
};

View File

@@ -5,8 +5,8 @@ export default async function Page({ params }: { params: { icao: string } }) {
const { data: airport } = await getAirport({ icao: params.icao });
return (
<>
<div className='border-b border-gray-200 bg-gray-400 px-4 py-5 sm:px-6 flex justify-between'>
<h3 className='text-base font-semibold leading-6 text-gray-900'>{airport.full_name}</h3>
<div className=''>
<h3 className=''>{airport.full_name}</h3>
<Link href={'/'}>Back</Link>
</div>
</>

View File

@@ -2,18 +2,20 @@ import React from 'react';
import RecoilRootWrapper from '@app/recoil-root-wrapper';
import Sidebar from '@/components/Sidebar';
import Topbar from '@/components/Topbar';
import { Inter } from 'next/font/google';
import { MantineProvider } from '@mantine/core';
import { ModalsProvider } from '@mantine/modals';
import 'styles/globals.css';
import 'styles/leaflet.css';
import StyledComponentsRegistry from '@/lib/AntdRegistry';
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'] });
import '@mantine/core/styles.css';
export const metadata = {
title: 'Aviation Weather',
description: ''
};
const inter = Inter({ subsets: ['latin'] });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='en' className='h-full bg-white'>
@@ -22,11 +24,13 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</head>
<body className={`${inter.className} wrapper h-full`}>
<RecoilRootWrapper>
<StyledComponentsRegistry>
<Topbar />
<Sidebar />
{children}
</StyledComponentsRegistry>
<MantineProvider>
<ModalsProvider>
<Topbar />
<Sidebar />
{children}
</ModalsProvider>
</MantineProvider>
</RecoilRootWrapper>
</body>
</html>

View File

@@ -1,3 +0,0 @@
export default function Profile() {
return <></>;
}

View File

@@ -7,7 +7,7 @@ import { DivIcon, LatLngBounds } from 'leaflet';
import { useEffect, useState } from 'react';
import ReactDOMServer from 'react-dom/server';
import { Marker, TileLayer, Tooltip, useMap, useMapEvents } from 'react-leaflet';
import MetarDialog from './MetarDialog';
import MetarModal from './MetarModal';
import { BsCircle, BsCircleFill } from 'react-icons/bs';
export default function MapTiles() {
@@ -133,7 +133,7 @@ export default function MapTiles() {
return (
<>
{selectedAirport && <MetarDialog isOpen={isOpen} onClose={() => setIsOpen(false)} airport={selectedAirport} />}
{selectedAirport && <MetarModal isOpen={isOpen} onClose={() => setIsOpen(false)} airport={selectedAirport} />}
<TileLayer
attribution='&copy; <a href="https://www.osm.org/copyright">OpenStreetMap</a> contributors'
url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'

View File

@@ -3,19 +3,19 @@
import { Airport } from '@/api/airport.types';
import { Metar } from '@/api/metar.types';
import { FaArrowsSpin, FaLocationArrow } from 'react-icons/fa6';
import { Col, Grid, Modal, Row, Tooltip } from 'antd';
import Link from 'next/link';
import { AiFillStar, AiOutlineStar } from 'react-icons/ai';
import { BsFillCloudRainFill, BsFillCloudRainHeavyFill, BsFillCloudSleetFill, BsFillCloudSnowFill, BsQuestionLg } from 'react-icons/bs';
import { useState } from 'react';
import { Grid, Modal, Tooltip } from '@mantine/core';
interface MetarDialogProps {
interface MetarModalProps {
airport: Airport;
isOpen: boolean;
onClose(): void;
}
export default function MetarDialog({ airport, isOpen, onClose }: MetarDialogProps) {
export default function MetarModal({ airport, isOpen, onClose }: MetarModalProps) {
const [isFavorite, setIsFavorite] = useState(false);
function handleFavorite(value: boolean) {
@@ -44,10 +44,8 @@ export default function MetarDialog({ airport, isOpen, onClose }: MetarDialogPro
)}
</span>
}
open={isOpen}
onCancel={onClose}
closable={false}
footer={[]}
opened={isOpen}
onClose={onClose}
className='select-none'
>
<div className='min-w-0 flex-1'>
@@ -131,8 +129,8 @@ function MetarInfo({ metar }: { metar: Metar }) {
return (
<div>
<p className='text-xs font-small text-gray-500'>{metar.raw_text}</p>
<Row gutter={18}>
<Col className='gutter-row' span={6}>
<Grid gutter={18}>
<Grid.Col className='gutter-row' span={6}>
<span
className={`text-sm text-white py-2 px-4 rounded-full
${metarBGColor(metar)}
@@ -140,15 +138,11 @@ function MetarInfo({ metar }: { metar: Metar }) {
>
{metar.flight_category ? metar.flight_category : 'UNKN'}
</span>
</Col>
<Col className='gutter-row' span={12}>
</Grid.Col>
<Grid.Col className='gutter-row' span={12}>
{metar.wx_string && metar.wx_string.split(' ').map((wx) => <MetarIcon wx={wx} />)}
</Col>
</Row>
<Row gutter={2}>Compass TBD Compass TBD Compass TBD Compass TBD Compass TB</Row>
<Row gutter={2}>
<Col></Col>
</Row>
</Grid.Col>
</Grid>
</div>
);
}
@@ -215,7 +209,7 @@ function MetarIcon({ wx }: { wx: string }) {
// color = '';
// }
return (
<Tooltip title={title}>
<Tooltip label={title}>
<span className={`rounded-full`}>{icon}</span>
</Tooltip>
);

View File

@@ -1,3 +1,5 @@
'use client';
import './Sidebar.css';
export default function Sidebar() {

View File

@@ -1,20 +1,18 @@
'use client';
import { AutoComplete, Avatar } from 'antd';
import Link from 'next/link';
import { AiOutlineUser } from 'react-icons/ai';
import { useState } from 'react';
import { getAirports } from '@/api/airport';
import { useRouter } from 'next/navigation';
const DEFAULT_ICON_SIZE = 40;
import { Autocomplete, Avatar } from '@mantine/core';
export default function Topbar() {
const [searchValue, setSearchValue] = useState('');
const [airports, setAirports] = useState<{ key: string; value: string; label: string }[]>([]);
const router = useRouter();
async function onSearch(value: string) {
async function onChange(value: string) {
setSearchValue(value);
const airportData = await getAirports({ filter: value });
setAirports(
@@ -26,34 +24,32 @@ export default function Topbar() {
);
}
function onSelect(value: string) {
setSearchValue('');
function onClick(value: string) {
router.push(`/airport/${value}`);
}
return (
<>
<nav className='w-screen flex bg-gray-700 text-gray-200 justify-between'>
<div className='flex'>
<Link href={'/'} className='align-middle pt-2.5 px-6 text-lg'>
<span>Aviation Weather</span>
</Link>
<AutoComplete
className='w-72 relative top-2'
autoFocus
defaultActiveFirstOption
value={searchValue}
options={airports}
onSelect={onSelect}
onSearch={onSearch}
onBlur={() => setSearchValue('')}
placeholder='Search Airports...'
/>
</div>
<Link className='my-1 mr-2' href={'/profile'}>
<Avatar shape='circle' size={DEFAULT_ICON_SIZE} icon={<AiOutlineUser />} />
<nav style={{ display: 'flex', justifyContent: 'space-between' }}>
<div style={{ display: 'flex' }}>
<Link href={'/'} style={{ paddingLeft: '2em', paddingRight: '2em', margin: 'auto' }}>
<span>Aviation Weather</span>
</Link>
</nav>
</>
<Autocomplete
autoFocus
radius='xl'
placeholder='Search Airports...'
limit={10}
data={airports}
value={searchValue}
onChange={onChange}
onBlur={() => setSearchValue('')}
/>
</div>
<Link className='' href={'/profile'}>
<Avatar>
<AiOutlineUser />
</Avatar>
</Link>
</nav>
);
}

View File

@@ -0,0 +1,5 @@
'use client';
import { createTheme } from '@mantine/core';
export const theme = createTheme({});

View File

@@ -1,14 +0,0 @@
'use client';
import React from 'react';
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import type Entity from '@ant-design/cssinjs/es/Cache';
import { useServerInsertedHTML } from 'next/navigation';
const StyledComponentsRegistry = ({ children }: { children: React.ReactNode }) => {
const cache = React.useMemo<Entity>(() => createCache(), [createCache]);
useServerInsertedHTML(() => <style id='antd' dangerouslySetInnerHTML={{ __html: extractStyle(cache, true) }} />);
return <StyleProvider cache={cache}>{children}</StyleProvider>;
};
export default StyledComponentsRegistry;

View File

@@ -1,7 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html,
body {
padding: 0;

View File

@@ -1,11 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./src/**/*.{js,ts,jsx,tsx,mdx}'],
theme: {
extend: {}
},
plugins: [],
corePlugins: {
preflight: false
}
};