diff --git a/frontend/src/features/storages/ui/edit/storages/EditFTPStorageComponent.tsx b/frontend/src/features/storages/ui/edit/storages/EditFTPStorageComponent.tsx
new file mode 100644
index 0000000..99c381b
--- /dev/null
+++ b/frontend/src/features/storages/ui/edit/storages/EditFTPStorageComponent.tsx
@@ -0,0 +1,260 @@
+import { DownOutlined, InfoCircleOutlined, UpOutlined } from '@ant-design/icons';
+import { Checkbox, Input, InputNumber, Tooltip } from 'antd';
+import { useState } from 'react';
+
+import type { Storage } from '../../../../../entity/storages';
+
+interface Props {
+ storage: Storage;
+ setStorage: (storage: Storage) => void;
+ setUnsaved: () => void;
+}
+
+export function EditFTPStorageComponent({ storage, setStorage, setUnsaved }: Props) {
+ const hasAdvancedValues =
+ !!storage?.ftpStorage?.skipTlsVerify || storage?.ftpStorage?.passiveMode === false;
+ const [showAdvanced, setShowAdvanced] = useState(hasAdvancedValues);
+
+ return (
+ <>
+
+
Host
+
{
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ host: e.target.value.trim(),
+ },
+ });
+ setUnsaved();
+ }}
+ size="small"
+ className="w-full max-w-[250px]"
+ placeholder="ftp.example.com"
+ />
+
+
+
+
Port
+
{
+ if (!storage?.ftpStorage || !value) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ port: value,
+ },
+ });
+ setUnsaved();
+ }}
+ size="small"
+ className="w-full max-w-[250px]"
+ min={1}
+ max={65535}
+ placeholder="21"
+ />
+
+
+
+
Username
+
{
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ username: e.target.value.trim(),
+ },
+ });
+ setUnsaved();
+ }}
+ size="small"
+ className="w-full max-w-[250px]"
+ placeholder="username"
+ />
+
+
+
+
Password
+
{
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ password: e.target.value,
+ },
+ });
+ setUnsaved();
+ }}
+ size="small"
+ className="w-full max-w-[250px]"
+ placeholder="password"
+ />
+
+
+
+
Path
+
+ {
+ if (!storage?.ftpStorage) return;
+
+ let pathValue = e.target.value.trim();
+ if (pathValue.startsWith('/')) {
+ pathValue = pathValue.substring(1);
+ }
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ path: pathValue || undefined,
+ },
+ });
+ setUnsaved();
+ }}
+ size="small"
+ className="w-full max-w-[250px]"
+ placeholder="backups (optional)"
+ />
+
+
+
+
+
+
+
+
+
Use SSL/TLS
+
+ {
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ useSsl: e.target.checked,
+ },
+ });
+ setUnsaved();
+ }}
+ >
+ Enable FTPS
+
+
+
+
+
+
+
+
+
+
setShowAdvanced(!showAdvanced)}
+ >
+ Advanced settings
+
+ {showAdvanced ? (
+
+ ) : (
+
+ )}
+
+
+
+ {showAdvanced && (
+ <>
+
+
Passive mode
+
+ {
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ passiveMode: e.target.checked,
+ },
+ });
+ setUnsaved();
+ }}
+ >
+ Use passive mode
+
+
+
+
+
+
+
+
+ {storage?.ftpStorage?.useSsl && (
+
+
Skip TLS verify
+
+ {
+ if (!storage?.ftpStorage) return;
+
+ setStorage({
+ ...storage,
+ ftpStorage: {
+ ...storage.ftpStorage,
+ skipTlsVerify: e.target.checked,
+ },
+ });
+ setUnsaved();
+ }}
+ >
+ Skip certificate verification
+
+
+
+
+
+
+
+ )}
+ >
+ )}
+
+
+ >
+ );
+}
diff --git a/frontend/src/features/storages/ui/show/ShowStorageComponent.tsx b/frontend/src/features/storages/ui/show/ShowStorageComponent.tsx
index 94b5d64..7c8321d 100644
--- a/frontend/src/features/storages/ui/show/ShowStorageComponent.tsx
+++ b/frontend/src/features/storages/ui/show/ShowStorageComponent.tsx
@@ -2,6 +2,7 @@ import { type Storage, StorageType } from '../../../../entity/storages';
import { getStorageLogoFromType } from '../../../../entity/storages/models/getStorageLogoFromType';
import { getStorageNameFromType } from '../../../../entity/storages/models/getStorageNameFromType';
import { ShowAzureBlobStorageComponent } from './storages/ShowAzureBlobStorageComponent';
+import { ShowFTPStorageComponent } from './storages/ShowFTPStorageComponent';
import { ShowGoogleDriveStorageComponent } from './storages/ShowGoogleDriveStorageComponent';
import { ShowNASStorageComponent } from './storages/ShowNASStorageComponent';
import { ShowS3StorageComponent } from './storages/ShowS3StorageComponent';
@@ -44,6 +45,10 @@ export function ShowStorageComponent({ storage }: Props) {
)}
+
+