Migrate server-related components to tailwind

This commit is contained in:
Alejandro Celaya
2025-04-01 11:48:35 +02:00
parent c462bc30e1
commit 06fac716d1
7 changed files with 38 additions and 31 deletions

View File

@@ -1,5 +1,7 @@
import type { TimeoutToggle } from '@shlinkio/shlink-frontend-kit'; import type { TimeoutToggle } from '@shlinkio/shlink-frontend-kit';
import { Result, useToggle } from '@shlinkio/shlink-frontend-kit'; import { useToggle } from '@shlinkio/shlink-frontend-kit';
import type { ResultProps } from '@shlinkio/shlink-frontend-kit/tailwind';
import { Result } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react'; import type { FC } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useNavigate } from 'react-router'; import { useNavigate } from 'react-router';
@@ -26,11 +28,11 @@ type CreateServerDeps = {
useTimeoutToggle: TimeoutToggle; useTimeoutToggle: TimeoutToggle;
}; };
const ImportResult = ({ type }: { type: 'error' | 'success' }) => ( const ImportResult = ({ variant }: Pick<ResultProps, 'variant'>) => (
<div className="mt-3"> <div className="mt-3">
<Result type={type}> <Result variant={variant}>
{type === 'success' && 'Servers properly imported. You can now select one from the list :)'} {variant === 'success' && 'Servers properly imported. You can now select one from the list :)'}
{type === 'error' && 'The servers could not be imported. Make sure the format is correct.'} {variant === 'error' && 'The servers could not be imported. Make sure the format is correct.'}
</Result> </Result>
</div> </div>
); );
@@ -68,16 +70,16 @@ const CreateServer: FCWithDeps<CreateServerProps, CreateServerDeps> = ({ servers
return ( return (
<NoMenuLayout> <NoMenuLayout>
<ServerForm title={<h5 className="mb-0">Add new server</h5>} onSubmit={onSubmit}> <ServerForm title="Add new server" onSubmit={onSubmit}>
{!hasServers && ( {!hasServers && (
<ImportServersBtn tooltipPlacement="top" onImport={setServersImported} onImportError={setErrorImporting} /> <ImportServersBtn tooltipPlacement="top" onImport={setServersImported} onImportError={setErrorImporting} />
)} )}
{hasServers && <Button outline onClick={goBack}>Cancel</Button>} {hasServers && <Button outline onClick={goBack}>Cancel</Button>}
<Button outline color="primary" className="ms-2">Create server</Button> <Button outline color="primary">Create server</Button>
</ServerForm> </ServerForm>
{serversImported && <ImportResult type="success" />} {serversImported && <ImportResult variant="success" />}
{errorImporting && <ImportResult type="error" />} {errorImporting && <ImportResult variant="error" />}
<DuplicatedServersModal <DuplicatedServersModal
isOpen={isConfirmModalOpen} isOpen={isConfirmModalOpen}

View File

@@ -1,6 +1,6 @@
import { useParsedQuery } from '@shlinkio/shlink-frontend-kit'; import { useParsedQuery } from '@shlinkio/shlink-frontend-kit';
import { Button } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react'; import type { FC } from 'react';
import { Button } from 'reactstrap';
import { NoMenuLayout } from '../common/NoMenuLayout'; import { NoMenuLayout } from '../common/NoMenuLayout';
import type { FCWithDeps } from '../container/utils'; import type { FCWithDeps } from '../container/utils';
import { componentFactory } from '../container/utils'; import { componentFactory } from '../container/utils';
@@ -40,12 +40,12 @@ const EditServer: FCWithDeps<EditServerProps, EditServerDeps> = withSelectedServ
return ( return (
<NoMenuLayout> <NoMenuLayout>
<ServerForm <ServerForm
title={<h5 className="mb-0">Edit &quot;{selectedServer.name}&quot;</h5>} title={<>Edit &quot;{selectedServer.name}&quot;</>}
initialValues={selectedServer} initialValues={selectedServer}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
<Button outline className="me-2" onClick={goBack}>Cancel</Button> <Button variant="secondary" onClick={goBack}>Cancel</Button>
<Button outline color="primary">Save</Button> <Button>Save</Button>
</ServerForm> </ServerForm>
</NoMenuLayout> </NoMenuLayout>
); );

View File

@@ -1,11 +1,9 @@
import { faFileDownload as exportIcon, faPlus as plusIcon } from '@fortawesome/free-solid-svg-icons'; import { faFileDownload as exportIcon, faPlus as plusIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import type { TimeoutToggle } from '@shlinkio/shlink-frontend-kit'; import type { TimeoutToggle } from '@shlinkio/shlink-frontend-kit';
import { Result, SearchField, SimpleCard } from '@shlinkio/shlink-frontend-kit'; import { Button, Result, SearchInput, SimpleCard } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react'; import type { FC } from 'react';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { Link } from 'react-router';
import { Button } from 'reactstrap';
import { NoMenuLayout } from '../common/NoMenuLayout'; import { NoMenuLayout } from '../common/NoMenuLayout';
import type { FCWithDeps } from '../container/utils'; import type { FCWithDeps } from '../container/utils';
import { componentFactory, useDependencies } from '../container/utils'; import { componentFactory, useDependencies } from '../container/utils';
@@ -46,18 +44,18 @@ const ManageServers: FCWithDeps<ManageServersProps, ManageServersDeps> = ({ serv
return ( return (
<NoMenuLayout className="d-flex flex-column gap-3"> <NoMenuLayout className="d-flex flex-column gap-3">
<SearchField onChange={setSearchTerm} /> <SearchInput onChange={setSearchTerm} />
<div className="d-flex flex-column flex-md-row gap-2"> <div className="d-flex flex-column flex-md-row gap-2">
<div className="d-flex gap-2"> <div className="d-flex gap-2">
<ImportServersBtn className="flex-fill" onImportError={setErrorImporting}>Import servers</ImportServersBtn> <ImportServersBtn className="flex-fill" onImportError={setErrorImporting}>Import servers</ImportServersBtn>
{filteredServers.length > 0 && ( {filteredServers.length > 0 && (
<Button outline className="flex-fill" onClick={async () => serversExporter.exportServers()}> <Button variant="secondary" className="flex-fill" onClick={async () => serversExporter.exportServers()}>
<FontAwesomeIcon icon={exportIcon} fixedWidth /> Export servers <FontAwesomeIcon icon={exportIcon} fixedWidth /> Export servers
</Button> </Button>
)} )}
</div> </div>
<Button outline color="primary" className="ms-md-auto" tag={Link} to="/server/create"> <Button className="ms-md-auto" to="/server/create">
<FontAwesomeIcon icon={plusIcon} fixedWidth /> Add a server <FontAwesomeIcon icon={plusIcon} fixedWidth /> Add a server
</Button> </Button>
</div> </div>
@@ -83,7 +81,7 @@ const ManageServers: FCWithDeps<ManageServersProps, ManageServersDeps> = ({ serv
{errorImporting && ( {errorImporting && (
<div> <div>
<Result type="error">The servers could not be imported. Make sure the format is correct.</Result> <Result variant="error">The servers could not be imported. Make sure the format is correct.</Result>
</div> </div>
)} )}
</NoMenuLayout> </NoMenuLayout>

View File

@@ -1,9 +1,10 @@
import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons'; import { faFileUpload as importIcon } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useElementRef, useToggle } from '@shlinkio/shlink-frontend-kit'; import { useElementRef, useToggle } from '@shlinkio/shlink-frontend-kit';
import { Button } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { ChangeEvent, PropsWithChildren } from 'react'; import type { ChangeEvent, PropsWithChildren } from 'react';
import { useCallback, useRef, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
import { Button, UncontrolledTooltip } from 'reactstrap'; import { UncontrolledTooltip } from 'reactstrap';
import type { FCWithDeps } from '../../container/utils'; import type { FCWithDeps } from '../../container/utils';
import { componentFactory, useDependencies } from '../../container/utils'; import { componentFactory, useDependencies } from '../../container/utils';
import type { ServerData, ServersMap, ServerWithId } from '../data'; import type { ServerData, ServersMap, ServerWithId } from '../data';
@@ -83,7 +84,7 @@ const ImportServersBtn: FCWithDeps<ImportServersBtnConnectProps, ImportServersBt
return ( return (
<> <>
<Button outline id="importBtn" className={className} onClick={() => ref.current?.click()}> <Button variant="secondary" id="importBtn" className={className} onClick={() => ref.current?.click()}>
<FontAwesomeIcon icon={importIcon} fixedWidth /> {children ?? 'Import from file'} <FontAwesomeIcon icon={importIcon} fixedWidth /> {children ?? 'Import from file'}
</Button> </Button>
<UncontrolledTooltip placement={tooltipPlacement} target="importBtn"> <UncontrolledTooltip placement={tooltipPlacement} target="importBtn">

View File

@@ -1,4 +1,4 @@
import { Message } from '@shlinkio/shlink-frontend-kit'; import { Message } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react'; import type { FC } from 'react';
import { Link } from 'react-router'; import { Link } from 'react-router';
import { NoMenuLayout } from '../../common/NoMenuLayout'; import { NoMenuLayout } from '../../common/NoMenuLayout';
@@ -25,7 +25,7 @@ const ServerError: FCWithDeps<ServerErrorProps, ServerErrorDeps> = ({ servers, s
return ( return (
<NoMenuLayout> <NoMenuLayout>
<div className="server-error__container flex-column"> <div className="server-error__container flex-column">
<Message className="w-100 mb-3 mb-md-5" type="error" fullWidth> <Message className="w-100 mb-3 mb-md-5" variant="error">
{!isServerWithId(selectedServer) && 'Could not find this Shlink server.'} {!isServerWithId(selectedServer) && 'Could not find this Shlink server.'}
{isServerWithId(selectedServer) && ( {isServerWithId(selectedServer) && (
<> <>

View File

@@ -1,4 +1,4 @@
import { InputFormGroup, SimpleCard } from '@shlinkio/shlink-frontend-kit'; import { LabelledInput, SimpleCard } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC, PropsWithChildren, ReactNode } from 'react'; import type { FC, PropsWithChildren, ReactNode } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { handleEventPreventingDefault } from '../../utils/utils'; import { handleEventPreventingDefault } from '../../utils/utils';
@@ -18,13 +18,19 @@ export const ServerForm: FC<ServerFormProps> = ({ onSubmit, initialValues, child
return ( return (
<form className="server-form" name="serverForm" onSubmit={handleSubmit}> <form className="server-form" name="serverForm" onSubmit={handleSubmit}>
<SimpleCard className="mb-3" title={title}> <SimpleCard className="tw:mb-4" bodyClassName="tw:flex tw:flex-col tw:gap-y-3" title={title}>
<InputFormGroup value={name} onChange={setName}>Name</InputFormGroup> <LabelledInput label="Name" value={name} onChange={(e) => setName(e.target.value)} required />
<InputFormGroup type="url" value={url} onChange={setUrl}>URL</InputFormGroup> <LabelledInput label="URL" type="url" value={url} onChange={(e) => setUrl(e.target.value)} required />
<InputFormGroup value={apiKey} onChange={setApiKey}>API key</InputFormGroup> <LabelledInput
label="API key"
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
required
/>
</SimpleCard> </SimpleCard>
<div className="text-end">{children}</div> <div className="tw:flex tw:items-center tw:justify-end tw:gap-x-2">{children}</div>
</form> </form>
); );
}; };

View File

@@ -1,4 +1,4 @@
import { Message } from '@shlinkio/shlink-frontend-kit'; import { Message } from '@shlinkio/shlink-frontend-kit/tailwind';
import type { FC } from 'react'; import type { FC } from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useParams } from 'react-router'; import { useParams } from 'react-router';