Enhance email domain management and switch to Monaco Editor
Updated EmailDomainsController to support password retrieval. Modified repository and service interfaces to include a new optional `returnPassword` parameter. Transitioned from Tiptap to Monaco Editor for rich text editing. Improved UI for EmailDomainEdit and TemplateEdit components, added password visibility toggle in EmailDomains, and refined data grid layout in Templates.
This commit is contained in:
parent
2bdb1a8de6
commit
d7b00cf335
@ -17,16 +17,19 @@ namespace Surge365.MassEmailReact.Server.Controllers
|
||||
}
|
||||
|
||||
[HttpGet("GetAll")]
|
||||
public async Task<IActionResult> GetAll([FromQuery] bool? activeOnly)
|
||||
public async Task<IActionResult> GetAll([FromQuery] bool? activeOnly, bool? returnPassword = null)
|
||||
{
|
||||
var emailDomains = await _emailDomainService.GetAllAsync(activeOnly == null || activeOnly.Value ? true : false);
|
||||
bool activeOnlyValue = activeOnly == null || activeOnly.Value ? true : false;
|
||||
bool returnPasswordValue = returnPassword != null && returnPassword.Value ? true : false;
|
||||
var emailDomains = await _emailDomainService.GetAllAsync(activeOnlyValue, returnPasswordValue);
|
||||
return Ok(emailDomains);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public async Task<IActionResult> GetByKey(int id)
|
||||
public async Task<IActionResult> GetByKey(int id, bool? returnPassword = null)
|
||||
{
|
||||
var emailDomain = await _emailDomainService.GetByIdAsync(id);
|
||||
bool returnPasswordValue = returnPassword != null && returnPassword.Value ? true : false;
|
||||
var emailDomain = await _emailDomainService.GetByIdAsync(id, returnPasswordValue);
|
||||
return emailDomain is not null ? Ok(emailDomain) : NotFound($"EmailDomain with key '{id}' not found.");
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ namespace Surge365.MassEmailReact.Application.Interfaces
|
||||
{
|
||||
public interface IEmailDomainRepository
|
||||
{
|
||||
Task<EmailDomain?> GetByIdAsync(int id);
|
||||
Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true);
|
||||
Task<EmailDomain?> GetByIdAsync(int id, bool returnPassword = false);
|
||||
Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true, bool returnPassword = false);
|
||||
Task<int?> CreateAsync(EmailDomain emailDomain);
|
||||
Task<bool> UpdateAsync(EmailDomain emailDomain);
|
||||
}
|
||||
|
||||
@ -6,8 +6,8 @@ namespace Surge365.MassEmailReact.Application.Interfaces
|
||||
{
|
||||
public interface IEmailDomainService
|
||||
{
|
||||
Task<EmailDomain?> GetByIdAsync(int id);
|
||||
Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true);
|
||||
Task<EmailDomain?> GetByIdAsync(int id, bool returnPassword = false);
|
||||
Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true, bool returnPassword = false);
|
||||
Task<int?> CreateAsync(EmailDomainUpdateDto emailDomainDto);
|
||||
Task<bool> UpdateAsync(EmailDomainUpdateDto emailDomainDto);
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Runtime.Intrinsics.Arm;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Surge365.MassEmailReact.Infrastructure.Repositories
|
||||
@ -35,22 +36,35 @@ namespace Surge365.MassEmailReact.Infrastructure.Repositories
|
||||
#endif
|
||||
}
|
||||
|
||||
public async Task<EmailDomain?> GetByIdAsync(int id)
|
||||
public async Task<EmailDomain?> GetByIdAsync(int id, bool returnPassword = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(_config);
|
||||
ArgumentNullException.ThrowIfNull(_connectionStringName);
|
||||
|
||||
using SqlConnection conn = new SqlConnection(ConnectionString);
|
||||
return (await conn.QueryAsync<EmailDomain>("mem_get_domain_by_id", new { domain_key = id }, commandType: CommandType.StoredProcedure)).FirstOrDefault();
|
||||
EmailDomain? domain = (await conn.QueryAsync<EmailDomain>("mem_get_domain_by_id", new { domain_key = id }, commandType: CommandType.StoredProcedure)).FirstOrDefault();
|
||||
if (domain != null && !returnPassword)
|
||||
{
|
||||
domain.Password = "";
|
||||
}
|
||||
return domain;
|
||||
}
|
||||
|
||||
public async Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true)
|
||||
public async Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true, bool returnPassword = false)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(_config);
|
||||
ArgumentNullException.ThrowIfNull(_connectionStringName);
|
||||
|
||||
using SqlConnection conn = new SqlConnection(ConnectionString);
|
||||
return (await conn.QueryAsync<EmailDomain>("mem_get_domain_all", new { active_only = activeOnly }, commandType: CommandType.StoredProcedure)).ToList();
|
||||
List<EmailDomain> domains = (await conn.QueryAsync<EmailDomain>("mem_get_domain_all", new { active_only = activeOnly }, commandType: CommandType.StoredProcedure)).ToList();
|
||||
if(!returnPassword)
|
||||
{
|
||||
foreach (var domain in domains)
|
||||
{
|
||||
domain.Password = "";
|
||||
}
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
public async Task<int?> CreateAsync(EmailDomain emailDomain)
|
||||
|
||||
@ -18,14 +18,14 @@ namespace Surge365.MassEmailReact.Infrastructure.Services
|
||||
_config = config;
|
||||
}
|
||||
|
||||
public async Task<EmailDomain?> GetByIdAsync(int id)
|
||||
public async Task<EmailDomain?> GetByIdAsync(int id, bool returnPassword = false)
|
||||
{
|
||||
return await _emailDomainRepository.GetByIdAsync(id);
|
||||
return await _emailDomainRepository.GetByIdAsync(id, returnPassword);
|
||||
}
|
||||
|
||||
public async Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true)
|
||||
public async Task<List<EmailDomain>> GetAllAsync(bool activeOnly = true, bool returnPassword = false)
|
||||
{
|
||||
return await _emailDomainRepository.GetAllAsync(activeOnly);
|
||||
return await _emailDomainRepository.GetAllAsync(activeOnly, returnPassword);
|
||||
}
|
||||
|
||||
public async Task<int?> CreateAsync(EmailDomainUpdateDto emailDomainDto)
|
||||
|
||||
748
Surge365.MassEmailReact.Web/package-lock.json
generated
748
Surge365.MassEmailReact.Web/package-lock.json
generated
@ -12,13 +12,11 @@
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@fontsource/roboto": "^5.1.1",
|
||||
"@hookform/resolvers": "^4.1.2",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@mui/icons-material": "^6.4.5",
|
||||
"@mui/material": "^6.4.5",
|
||||
"@mui/x-charts": "^7.27.1",
|
||||
"@mui/x-data-grid": "^7.27.1",
|
||||
"@tiptap/pm": "^2.11.5",
|
||||
"@tiptap/react": "^2.11.5",
|
||||
"@tiptap/starter-kit": "^2.11.5",
|
||||
"admin-lte": "4.0.0-beta3",
|
||||
"bootstrap": "^5.3.3",
|
||||
"dayjs": "^1.11.13",
|
||||
@ -1195,6 +1193,29 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/loader": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.5.0.tgz",
|
||||
"integrity": "sha512-hKoGSM+7aAc7eRTRjpqAZucPmoNOC4UUbknb/VNoTkEIkCPhqV8LfbsgM1webRM7S/z21eHEx9Fkwx8Z/C/+Xw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"state-local": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@monaco-editor/react": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz",
|
||||
"integrity": "sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@monaco-editor/loader": "^1.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"monaco-editor": ">= 0.25.0 < 1",
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "6.4.5",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.5.tgz",
|
||||
@ -1688,12 +1709,6 @@
|
||||
"integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@remirror/core-constants": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
|
||||
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@restart/hooks": {
|
||||
"version": "0.4.16",
|
||||
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
|
||||
@ -2042,391 +2057,6 @@
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/core": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.11.5.tgz",
|
||||
"integrity": "sha512-jb0KTdUJaJY53JaN7ooY3XAxHQNoMYti/H6ANo707PsLXVeEqJ9o8+eBup1JU5CuwzrgnDc2dECt2WIGX9f8Jw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-blockquote": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.11.5.tgz",
|
||||
"integrity": "sha512-MZfcRIzKRD8/J1hkt/eYv49060GTL6qGR3NY/oTDuw2wYzbQXXLEbjk8hxAtjwNn7G+pWQv3L+PKFzZDxibLuA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bold": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.11.5.tgz",
|
||||
"integrity": "sha512-OAq03MHEbl7MtYCUzGuwb0VpOPnM0k5ekMbEaRILFU5ZC7cEAQ36XmPIw1dQayrcuE8GZL35BKub2qtRxyC9iA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bubble-menu": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.11.5.tgz",
|
||||
"integrity": "sha512-rx+rMd7EEdht5EHLWldpkzJ56SWYA9799b33ustePqhXd6linnokJCzBqY13AfZ9+xp3RsR6C0ZHI9GGea0tIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-bullet-list": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.11.5.tgz",
|
||||
"integrity": "sha512-VXwHlX6A/T6FAspnyjbKDO0TQ+oetXuat6RY1/JxbXphH42nLuBaGWJ6pgy6xMl6XY8/9oPkTNrfJw/8/eeRwA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.11.5.tgz",
|
||||
"integrity": "sha512-xOvHevNIQIcCCVn9tpvXa1wBp0wHN/2umbAZGTVzS+AQtM7BTo0tz8IyzwxkcZJaImONcUVYLOLzt2AgW1LltA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-code-block": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.11.5.tgz",
|
||||
"integrity": "sha512-ksxMMvqLDlC+ftcQLynqZMdlJT1iHYZorXsXw/n+wuRd7YElkRkd6YWUX/Pq/njFY6lDjKiqFLEXBJB8nrzzBA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-document": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.11.5.tgz",
|
||||
"integrity": "sha512-7I4BRTpIux2a0O2qS3BDmyZ5LGp3pszKbix32CmeVh7lN9dV7W5reDqtJJ9FCZEEF+pZ6e1/DQA362dflwZw2g==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-dropcursor": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.11.5.tgz",
|
||||
"integrity": "sha512-uIN7L3FU0904ec7FFFbndO7RQE/yiON4VzAMhNn587LFMyWO8US139HXIL4O8dpZeYwYL3d1FnDTflZl6CwLlg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-floating-menu": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.11.5.tgz",
|
||||
"integrity": "sha512-HsMI0hV5Lwzm530Z5tBeyNCBNG38eJ3qjfdV2OHlfSf3+KOEfn6a5AUdoNaZO02LF79/8+7BaYU2drafag9cxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tippy.js": "^6.3.7"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-gapcursor": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.11.5.tgz",
|
||||
"integrity": "sha512-kcWa+Xq9cb6lBdiICvLReuDtz/rLjFKHWpW3jTTF3FiP3wx4H8Rs6bzVtty7uOVTfwupxZRiKICAMEU6iT0xrQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-hard-break": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.11.5.tgz",
|
||||
"integrity": "sha512-q9doeN+Yg9F5QNTG8pZGYfNye3tmntOwch683v0CCVCI4ldKaLZ0jG3NbBTq+mosHYdgOH2rNbIORlRRsQ+iYQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-heading": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.11.5.tgz",
|
||||
"integrity": "sha512-x/MV53psJ9baRcZ4k4WjnCUBMt8zCX7mPlKVT+9C/o+DEs/j/qxPLs95nHeQv70chZpSwCQCt93xMmuF0kPoAg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-history": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.11.5.tgz",
|
||||
"integrity": "sha512-b+wOS33Dz1azw6F1i9LFTEIJ/gUui0Jwz5ZvmVDpL2ZHBhq1Ui0/spTT+tuZOXq7Y/uCbKL8Liu4WoedIvhboQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-horizontal-rule": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.11.5.tgz",
|
||||
"integrity": "sha512-3up2r1Du8/5/4ZYzTC0DjTwhgPI3dn8jhOCLu73m5F3OGvK/9whcXoeWoX103hYMnGDxBlfOje71yQuN35FL4A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-italic": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.11.5.tgz",
|
||||
"integrity": "sha512-9VGfb2/LfPhQ6TjzDwuYLRvw0A6VGbaIp3F+5Mql8XVdTBHb2+rhELbyhNGiGVR78CaB/EiKb6dO9xu/tBWSYA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-list-item": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.11.5.tgz",
|
||||
"integrity": "sha512-Mp5RD/pbkfW1vdc6xMVxXYcta73FOwLmblQlFNn/l/E5/X1DUSA4iGhgDDH4EWO3swbs03x2f7Zka/Xoj3+WLg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-ordered-list": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.11.5.tgz",
|
||||
"integrity": "sha512-Cu8KwruBNWAaEfshRQR0yOSaUKAeEwxW7UgbvF9cN/zZuKgK5uZosPCPTehIFCcRe+TBpRtZQh+06f/gNYpYYg==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-paragraph": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.11.5.tgz",
|
||||
"integrity": "sha512-YFBWeg7xu/sBnsDIF/+nh9Arf7R0h07VZMd0id5Ydd2Qe3c1uIZwXxeINVtH0SZozuPIQFAT8ICe9M0RxmE+TA==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-strike": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.11.5.tgz",
|
||||
"integrity": "sha512-PVfUiCqrjvsLpbIoVlegSY8RlkR64F1Rr2RYmiybQfGbg+AkSZXDeO0eIrc03//4gua7D9DfIozHmAKv1KN3ow==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.11.5.tgz",
|
||||
"integrity": "sha512-Gq1WwyhFpCbEDrLPIHt5A8aLSlf8bfz4jm417c8F/JyU0J5dtYdmx0RAxjnLw1i7ZHE7LRyqqAoS0sl7JHDNSQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/extension-text-style": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.11.5.tgz",
|
||||
"integrity": "sha512-YUmYl0gILSd/u/ZkOmNxjNXVw+mu8fpC2f8G4I4tLODm0zCx09j9DDEJXSrM5XX72nxJQqtSQsCpNKnL0hfeEQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/pm": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.11.5.tgz",
|
||||
"integrity": "sha512-z9JFtqc5ZOsdQLd9vRnXfTCQ8v5ADAfRt9Nm7SqP6FUHII8E1hs38ACzf5xursmth/VonJYb5+73Pqxk1hGIPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-changeset": "^2.2.1",
|
||||
"prosemirror-collab": "^1.3.1",
|
||||
"prosemirror-commands": "^1.6.2",
|
||||
"prosemirror-dropcursor": "^1.8.1",
|
||||
"prosemirror-gapcursor": "^1.3.2",
|
||||
"prosemirror-history": "^1.4.1",
|
||||
"prosemirror-inputrules": "^1.4.0",
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-markdown": "^1.13.1",
|
||||
"prosemirror-menu": "^1.2.4",
|
||||
"prosemirror-model": "^1.23.0",
|
||||
"prosemirror-schema-basic": "^1.2.3",
|
||||
"prosemirror-schema-list": "^1.4.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-tables": "^1.6.3",
|
||||
"prosemirror-trailing-node": "^3.0.0",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.37.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/react": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.11.5.tgz",
|
||||
"integrity": "sha512-Dp8eHL1G+R/C4+QzAczyb3t1ovexEIZx9ln7SGEM+cT1KHKAw9XGPRgsp92+NQaYI+EdEb/YqoBOSzQcd18/OQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/extension-bubble-menu": "^2.11.5",
|
||||
"@tiptap/extension-floating-menu": "^2.11.5",
|
||||
"@types/use-sync-external-store": "^0.0.6",
|
||||
"fast-deep-equal": "^3",
|
||||
"use-sync-external-store": "^1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@tiptap/core": "^2.7.0",
|
||||
"@tiptap/pm": "^2.7.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tiptap/starter-kit": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.11.5.tgz",
|
||||
"integrity": "sha512-SLI7Aj2ruU1t//6Mk8f+fqW+18uTqpdfLUJYgwu0CkqBckrkRZYZh6GVLk/02k3H2ki7QkFxiFbZrdbZdng0JA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tiptap/core": "^2.11.5",
|
||||
"@tiptap/extension-blockquote": "^2.11.5",
|
||||
"@tiptap/extension-bold": "^2.11.5",
|
||||
"@tiptap/extension-bullet-list": "^2.11.5",
|
||||
"@tiptap/extension-code": "^2.11.5",
|
||||
"@tiptap/extension-code-block": "^2.11.5",
|
||||
"@tiptap/extension-document": "^2.11.5",
|
||||
"@tiptap/extension-dropcursor": "^2.11.5",
|
||||
"@tiptap/extension-gapcursor": "^2.11.5",
|
||||
"@tiptap/extension-hard-break": "^2.11.5",
|
||||
"@tiptap/extension-heading": "^2.11.5",
|
||||
"@tiptap/extension-history": "^2.11.5",
|
||||
"@tiptap/extension-horizontal-rule": "^2.11.5",
|
||||
"@tiptap/extension-italic": "^2.11.5",
|
||||
"@tiptap/extension-list-item": "^2.11.5",
|
||||
"@tiptap/extension-ordered-list": "^2.11.5",
|
||||
"@tiptap/extension-paragraph": "^2.11.5",
|
||||
"@tiptap/extension-strike": "^2.11.5",
|
||||
"@tiptap/extension-text": "^2.11.5",
|
||||
"@tiptap/extension-text-style": "^2.11.5",
|
||||
"@tiptap/pm": "^2.11.5"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ueberdosis"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
@ -2543,28 +2173,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/markdown-it": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
|
||||
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/linkify-it": "^5",
|
||||
"@types/mdurl": "^2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.4.tgz",
|
||||
@ -2614,12 +2222,6 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/warning": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
|
||||
@ -2931,6 +2533,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||
"dev": true,
|
||||
"license": "Python-2.0"
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
@ -3161,12 +2764,6 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/crelt": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
|
||||
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@ -3368,18 +2965,6 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/entities": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
@ -3633,6 +3218,7 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-glob": {
|
||||
@ -4091,15 +3677,6 @@
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/locate-path": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
|
||||
@ -4145,29 +3722,6 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/markdown-it": {
|
||||
"version": "14.1.0",
|
||||
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
||||
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1",
|
||||
"entities": "^4.4.0",
|
||||
"linkify-it": "^5.0.0",
|
||||
"mdurl": "^2.0.0",
|
||||
"punycode.js": "^2.3.1",
|
||||
"uc.micro": "^2.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"markdown-it": "bin/markdown-it.mjs"
|
||||
}
|
||||
},
|
||||
"node_modules/mdurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@ -4205,6 +3759,13 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/monaco-editor": {
|
||||
"version": "0.52.2",
|
||||
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz",
|
||||
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@ -4271,12 +3832,6 @@
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/orderedmap": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
|
||||
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/p-limit": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
|
||||
@ -4462,201 +4017,6 @@
|
||||
"integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prosemirror-changeset": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz",
|
||||
"integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-collab": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
|
||||
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-commands": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.0.tgz",
|
||||
"integrity": "sha512-6toodS4R/Aah5pdsrIwnTYPEjW70SlO5a66oo5Kk+CIrgJz3ukOoS+FYDGqvQlAX5PxoGWDX1oD++tn5X3pyRA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-dropcursor": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz",
|
||||
"integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0",
|
||||
"prosemirror-view": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-gapcursor": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
|
||||
"integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.0.0",
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-view": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-history": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz",
|
||||
"integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.2.2",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.31.0",
|
||||
"rope-sequence": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-inputrules": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz",
|
||||
"integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-keymap": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz",
|
||||
"integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"w3c-keyname": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-markdown": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.1.tgz",
|
||||
"integrity": "sha512-Sl+oMfMtAjWtlcZoj/5L/Q39MpEnVZ840Xo330WJWUvgyhNmLBLN7MsHn07s53nG/KImevWHSE6fEj4q/GihHw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/markdown-it": "^14.0.0",
|
||||
"markdown-it": "^14.0.0",
|
||||
"prosemirror-model": "^1.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-menu": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz",
|
||||
"integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"crelt": "^1.0.0",
|
||||
"prosemirror-commands": "^1.0.0",
|
||||
"prosemirror-history": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-model": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.24.1.tgz",
|
||||
"integrity": "sha512-YM053N+vTThzlWJ/AtPtF1j0ebO36nvbmDy4U7qA2XQB8JVaQp1FmB9Jhrps8s+z+uxhhVTny4m20ptUvhk0Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"orderedmap": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-basic": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.3.tgz",
|
||||
"integrity": "sha512-h+H0OQwZVqMon1PNn0AG9cTfx513zgIG2DY00eJ00Yvgb3UD+GQ/VlWW5rcaxacpCGT1Yx8nuhwXk4+QbXUfJA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-schema-list": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.0.tgz",
|
||||
"integrity": "sha512-gg1tAfH1sqpECdhIHOA/aLg2VH3ROKBWQ4m8Qp9mBKrOxQRW61zc+gMCI8nh22gnBzd1t2u1/NPLmO3nAa3ssg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-state": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
|
||||
"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.0.0",
|
||||
"prosemirror-transform": "^1.0.0",
|
||||
"prosemirror-view": "^1.27.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-tables": {
|
||||
"version": "1.6.4",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.6.4.tgz",
|
||||
"integrity": "sha512-TkDY3Gw52gRFRfRn2f4wJv5WOgAOXLJA2CQJYIJ5+kdFbfj3acR4JUW6LX2e1hiEBiUwvEhzH5a3cZ5YSztpIA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-keymap": "^1.2.2",
|
||||
"prosemirror-model": "^1.24.1",
|
||||
"prosemirror-state": "^1.4.3",
|
||||
"prosemirror-transform": "^1.10.2",
|
||||
"prosemirror-view": "^1.37.2"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-trailing-node": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
|
||||
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remirror/core-constants": "3.0.0",
|
||||
"escape-string-regexp": "^4.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prosemirror-model": "^1.22.1",
|
||||
"prosemirror-state": "^1.4.2",
|
||||
"prosemirror-view": "^1.33.8"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-transform": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.2.tgz",
|
||||
"integrity": "sha512-2iUq0wv2iRoJO/zj5mv8uDUriOHWzXRnOTVgCzSXnktS/2iQRa3UUQwVlkBlYZFtygw6Nh1+X4mGqoYBINn5KQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prosemirror-view": {
|
||||
"version": "1.38.0",
|
||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.38.0.tgz",
|
||||
"integrity": "sha512-O45kxXQTaP9wPdXhp8TKqCR+/unS/gnfg9Q93svQcB3j0mlp2XSPAmsPefxHADwzC+fbNS404jqRxm3UQaGvgw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prosemirror-model": "^1.20.0",
|
||||
"prosemirror-state": "^1.0.0",
|
||||
"prosemirror-transform": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
@ -4667,15 +4027,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/punycode.js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||
@ -4949,12 +4300,6 @@
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/rope-sequence": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
|
||||
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
@ -5043,6 +4388,12 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/state-local": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz",
|
||||
"integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/strip-json-comments": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
|
||||
@ -5093,15 +4444,6 @@
|
||||
"integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tippy.js": {
|
||||
"version": "6.3.7",
|
||||
"resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
|
||||
"integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
@ -5208,12 +4550,6 @@
|
||||
"typescript": ">=4.8.4 <5.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uc.micro": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uncontrollable": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
|
||||
@ -5358,12 +4694,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/w3c-keyname": {
|
||||
"version": "2.2.8",
|
||||
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/warning": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
|
||||
|
||||
@ -15,13 +15,11 @@
|
||||
"@emotion/styled": "^11.14.0",
|
||||
"@fontsource/roboto": "^5.1.1",
|
||||
"@hookform/resolvers": "^4.1.2",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@mui/icons-material": "^6.4.5",
|
||||
"@mui/material": "^6.4.5",
|
||||
"@mui/x-charts": "^7.27.1",
|
||||
"@mui/x-data-grid": "^7.27.1",
|
||||
"@tiptap/pm": "^2.11.5",
|
||||
"@tiptap/react": "^2.11.5",
|
||||
"@tiptap/starter-kit": "^2.11.5",
|
||||
"admin-lte": "4.0.0-beta3",
|
||||
"bootstrap": "^5.3.3",
|
||||
"dayjs": "^1.11.13",
|
||||
|
||||
@ -19,7 +19,7 @@ type EmailDomainEditProps = {
|
||||
open: boolean;
|
||||
emailDomain: EmailDomain | null;
|
||||
onClose: () => void;
|
||||
onSave: (updatedEmailDomain: EmailDomain) => void;
|
||||
onSave: (updatedEmailDomain: EmailDomain, password: string) => void;
|
||||
};
|
||||
|
||||
const schema = yup.object().shape({
|
||||
@ -34,9 +34,13 @@ const schema = yup.object().shape({
|
||||
(d) => d.name.toLowerCase() === value?.toLowerCase() && (d.id === 0 || d.id !== this.parent.id)
|
||||
);
|
||||
}),
|
||||
emailAddress: yup.string().required("Email address is required"),
|
||||
emailAddress: yup.string().email("Invalid email").required("Email address is required"),
|
||||
username: yup.string().required("Username is required"),
|
||||
password: yup.string().required("Password is required"),
|
||||
password: yup.string().default("")
|
||||
.test("required-if-new", "NamePassword is required", function (value) {
|
||||
if (this.parent.id > 0) return true;
|
||||
else return value.length > 0;
|
||||
}),
|
||||
isActive: yup.boolean().default(true),
|
||||
displayOrder: yup.number().required("Display order is required"),
|
||||
});
|
||||
@ -54,6 +58,7 @@ const defaultEmailDomain: EmailDomain = {
|
||||
const EmailDomainEdit = ({ open, emailDomain, onClose, onSave }: EmailDomainEditProps) => {
|
||||
const isNew = !emailDomain || emailDomain.id === 0;
|
||||
const setupData: SetupData = useSetupData();
|
||||
const originalEmailDomain: EmailDomain | null = emailDomain ? { ...emailDomain } : null;
|
||||
|
||||
const { register, trigger, control, handleSubmit, reset, formState: { errors } } = useForm<EmailDomain>({
|
||||
mode: "onBlur",
|
||||
@ -84,7 +89,7 @@ const EmailDomainEdit = ({ open, emailDomain, onClose, onSave }: EmailDomainEdit
|
||||
if (!response.ok) throw new Error(isNew ? "Failed to create" : "Failed to update");
|
||||
|
||||
const updatedEmailDomain = await response.json();
|
||||
onSave(updatedEmailDomain);
|
||||
onSave(updatedEmailDomain, isNew || formData.password ? formData.password : originalEmailDomain?.password);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Update error:", error);
|
||||
|
||||
@ -9,12 +9,14 @@ import {
|
||||
Button,
|
||||
Switch,
|
||||
FormControlLabel,
|
||||
Grid,
|
||||
Box,
|
||||
Paper,
|
||||
} from "@mui/material";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import * as yup from "yup";
|
||||
import { EditorContent, useEditor, Editor } from '@tiptap/react';
|
||||
import StarterKit from '@tiptap/starter-kit';
|
||||
import Editor from "@monaco-editor/react";
|
||||
|
||||
// Assuming these types and context are defined elsewhere
|
||||
import Template from "@/types/template";
|
||||
@ -31,13 +33,13 @@ const schema = yup.object().shape({
|
||||
id: yup.number().default(0),
|
||||
name: yup.string().required("Name is required"),
|
||||
domainId: yup.number().required("Domain is required").moreThan(0, "Domain is required"),
|
||||
description: yup.string().required("Description is required"),
|
||||
description: yup.string().default(""),
|
||||
htmlBody: yup.string().required("HTML Body is required"),
|
||||
subject: yup.string().required("Subject is required"),
|
||||
toName: yup.string().default(""),
|
||||
fromName: yup.string().required("From Name is required"),
|
||||
fromEmail: yup.string().email("Invalid email").required("From Email is required"),
|
||||
replyToEmail: yup.string().email("Invalid email").required("Reply To Email is required"),
|
||||
fromEmail: yup.string().default(""),
|
||||
replyToEmail: yup.string().default(""),
|
||||
clickTracking: yup.boolean().default(false),
|
||||
openTracking: yup.boolean().default(false),
|
||||
categoryXml: yup.string().default(""),
|
||||
@ -71,6 +73,7 @@ const TemplateEdit = ({ open, template, onClose, onSave }: TemplateEditProps) =>
|
||||
handleSubmit,
|
||||
reset,
|
||||
setValue,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm<Template>({
|
||||
mode: "onBlur",
|
||||
@ -80,23 +83,19 @@ const TemplateEdit = ({ open, template, onClose, onSave }: TemplateEditProps) =>
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const editor = useEditor({
|
||||
extensions: [StarterKit],
|
||||
content: template?.htmlBody || '',
|
||||
onUpdate: ({ editor }) => {
|
||||
const html = editor.getHTML();
|
||||
setValue('htmlBody', html, { shouldValidate: true });
|
||||
},
|
||||
});
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const [isPreviewMode, setIsPreviewMode] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (open && editor) {
|
||||
editor.commands.setContent(template?.htmlBody || '');
|
||||
if (open) {
|
||||
reset(template || defaultTemplate, { keepDefaultValues: true });
|
||||
}
|
||||
}, [open, template, editor]);
|
||||
}, [open, template, reset]);
|
||||
|
||||
const handleSave = async (formData: Template) => {
|
||||
const domain = setupData.emailDomains.find(el => el.id === formData.domainId);
|
||||
formData.fromEmail = domain.emailAddress;
|
||||
formData.replyToEmail = domain.emailAddress;
|
||||
const apiUrl = isNew ? "/api/templates" : `/api/templates/${formData.id}`;
|
||||
const method = isNew ? "POST" : "PUT";
|
||||
setLoading(true);
|
||||
@ -119,154 +118,189 @@ const TemplateEdit = ({ open, template, onClose, onSave }: TemplateEditProps) =>
|
||||
}
|
||||
};
|
||||
|
||||
type MenuProps = {
|
||||
editor: Editor | null;
|
||||
};
|
||||
const MenuBar = ({ editor }: MenuProps) => {
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleBold().run()}
|
||||
disabled={!editor.can().toggleBold()}
|
||||
>
|
||||
Bold
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleItalic().run()}
|
||||
disabled={!editor.can().toggleItalic()}
|
||||
>
|
||||
Italic
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleBulletList().run()}
|
||||
>
|
||||
Bullet List
|
||||
</button>
|
||||
<button
|
||||
onClick={() => editor.chain().focus().toggleOrderedList().run()}
|
||||
>
|
||||
Ordered List
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
const renderEditorOrPreview = (height: string) => (
|
||||
<Box sx={{ flexGrow: 1, overflow: "hidden" }}>
|
||||
{isPreviewMode ? (
|
||||
<iframe
|
||||
srcDoc={watch("htmlBody")}
|
||||
sandbox="allow-same-origin"
|
||||
style={{ width: "100%", height, border: "none" }}
|
||||
/>
|
||||
) : (
|
||||
<Editor
|
||||
height={height}
|
||||
defaultLanguage="html"
|
||||
value={watch("htmlBody")}
|
||||
onChange={(value) => setValue("htmlBody", value || "", { shouldValidate: true })}
|
||||
options={{ wordWrap: "on" }}
|
||||
/>
|
||||
)}
|
||||
{!isExpanded && errors.htmlBody && (
|
||||
<p style={{ color: "red", marginTop: "8px" }}>{errors.htmlBody.message}</p>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||
<DialogTitle>{isNew ? "Add Template" : "Edit Template id=" + template?.id}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Controller
|
||||
name="domainId"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
options={setupData.emailDomains}
|
||||
getOptionLabel={(option) => option.name}
|
||||
value={setupData.emailDomains.find((d) => d.id === field.value) || null}
|
||||
onChange={(_, newValue) => field.onChange(newValue ? newValue.id : null)}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Domain"
|
||||
margin="dense"
|
||||
error={!!errors.domainId}
|
||||
helperText={errors.domainId?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
{...register("name")}
|
||||
label="Name"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register("description")}
|
||||
label="Description"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
<Controller
|
||||
name="htmlBody"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<div>
|
||||
<MenuBar editor={editor} />
|
||||
<EditorContent editor={editor} />
|
||||
{errors.htmlBody && (
|
||||
<p style={{ color: 'red' }}>{errors.htmlBody.message}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<TextField
|
||||
{...register("subject")}
|
||||
label="Subject"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.subject}
|
||||
helperText={errors.subject?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register("fromName")}
|
||||
label="From Name"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.fromName}
|
||||
helperText={errors.fromName?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register("fromEmail")}
|
||||
label="From Email"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.fromEmail}
|
||||
helperText={errors.fromEmail?.message}
|
||||
/>
|
||||
<TextField
|
||||
{...register("replyToEmail")}
|
||||
label="Reply To Email"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
error={!!errors.replyToEmail}
|
||||
helperText={errors.replyToEmail?.message}
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={<Switch {...register("clickTracking")} />}
|
||||
label="Click Tracking"
|
||||
/>
|
||||
<FormControlLabel
|
||||
control={<Switch {...register("openTracking")} />}
|
||||
label="Open Tracking"
|
||||
/>
|
||||
<TextField
|
||||
{...register("categoryXml")}
|
||||
label="Category XML"
|
||||
fullWidth
|
||||
margin="dense"
|
||||
multiline
|
||||
rows={4}
|
||||
error={!!errors.categoryXml}
|
||||
helperText={errors.categoryXml?.message}
|
||||
/>
|
||||
<Controller
|
||||
name="isActive"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={<Switch checked={field.value} onChange={(e) => field.onChange(e.target.checked)} />}
|
||||
label="Active"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
maxWidth={isExpanded ? false : "lg"}
|
||||
fullScreen={isExpanded}
|
||||
sx={{ "& .MuiDialog-paper": { width: isExpanded ? "100%" : "auto" } }}
|
||||
>
|
||||
<DialogTitle>
|
||||
{isExpanded
|
||||
? "Editing HTML Body"
|
||||
: isNew
|
||||
? "Add Template"
|
||||
: `Edit Template id=${template?.id}`}
|
||||
</DialogTitle>
|
||||
<DialogContent sx={{ p: isExpanded ? 0 : 2, display: "flex", flexDirection: "column" }}>
|
||||
{isExpanded ? (
|
||||
<Box sx={{ height: "100%", display: "flex", flexDirection: "column" }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 1,
|
||||
backgroundColor: "#f5f5f5",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Button onClick={() => setIsExpanded(false)}>Collapse</Button>
|
||||
<Button onClick={() => setIsPreviewMode(!isPreviewMode)}>
|
||||
{isPreviewMode ? "Edit" : "Preview"}
|
||||
</Button>
|
||||
</Box>
|
||||
{renderEditorOrPreview("calc(100vh - 120px)")}
|
||||
</Box>
|
||||
) : (
|
||||
<Grid container spacing={2}>
|
||||
{/* Top Section: Two Columns for All Fields Except HTML */}
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={2} sx={{ marginTop: 0 }}>
|
||||
<Grid item xs={6} >
|
||||
<TextField
|
||||
{...register("name")}
|
||||
label="Name"
|
||||
fullWidth
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
{...register("description")}
|
||||
label="Description"
|
||||
fullWidth
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
{...register("subject")}
|
||||
label="Subject"
|
||||
fullWidth
|
||||
error={!!errors.subject}
|
||||
helperText={errors.subject?.message}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<TextField
|
||||
{...register("fromName")}
|
||||
label="From Name"
|
||||
fullWidth
|
||||
error={!!errors.fromName}
|
||||
helperText={errors.fromName?.message}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={6}>
|
||||
<Controller
|
||||
name="domainId"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<Autocomplete
|
||||
options={setupData.emailDomains}
|
||||
getOptionLabel={(option) => option.name}
|
||||
value={setupData.emailDomains.find((d) => d.id === field.value) || null}
|
||||
onChange={(_, newValue) => field.onChange(newValue ? newValue.id : null)}
|
||||
renderInput={(params) => (
|
||||
<TextField
|
||||
{...params}
|
||||
label="Domain"
|
||||
error={!!errors.domainId}
|
||||
helperText={errors.domainId?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Controller
|
||||
name="clickTracking"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onChange={(e) => field.onChange(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Click Tracking"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="openTracking"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onChange={(e) => field.onChange(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Open Tracking"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="isActive"
|
||||
control={control}
|
||||
render={({ field }) => (
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
checked={field.value}
|
||||
onChange={(e) => field.onChange(e.target.checked)}
|
||||
/>
|
||||
}
|
||||
label="Active"
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{/* Bottom Section: Single Column for HTML Control */}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between", my: 1 }}>
|
||||
<Button onClick={() => setIsExpanded(true)}>Expand Editor</Button>
|
||||
<Button onClick={() => setIsPreviewMode(!isPreviewMode)}>
|
||||
{isPreviewMode ? "Edit" : "Preview"}
|
||||
</Button>
|
||||
</Box>
|
||||
<Paper variant="outlined" sx={{ p: 2 }}>
|
||||
{renderEditorOrPreview("300px")}
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
)}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} disabled={loading}>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { useState, useRef } from 'react';
|
||||
import { useSetupData, SetupData } from "@/context/SetupDataContext";
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import { Lock, LockOpen } from "@mui/icons-material";
|
||||
import { List, Card, CardContent, Typography, Box, useTheme, useMediaQuery, Button, CircularProgress, IconButton } from '@mui/material';
|
||||
import { DataGrid, GridColDef, GridRenderCellParams, GridRowModel, GridToolbarContainer, GridToolbarQuickFilter, GridToolbarExport, GridToolbarDensitySelector, GridToolbarColumnsButton } from '@mui/x-data-grid';
|
||||
import EmailDomain from '@/types/emailDomain';
|
||||
@ -11,6 +12,8 @@ function EmailDomains() {
|
||||
const setupData: SetupData = useSetupData();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
|
||||
const [emailDomainsWithPasswords, setEmailDomainsWithPasswords] = useState<EmailDomain[] | null>(null);
|
||||
const [isPasswordVisible, setIsPasswordVisible] = useState(false);
|
||||
const gridContainerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [selectedRow, setSelectedRow] = useState<EmailDomain | null>(null);
|
||||
const [open, setOpen] = useState<boolean>(false);
|
||||
@ -30,11 +33,43 @@ function EmailDomains() {
|
||||
{ field: "name", headerName: "Name", flex: 1, minWidth: 160 },
|
||||
{ field: "emailAddress", headerName: "Email Address", flex: 1, minWidth: 200 },
|
||||
{ field: "username", headerName: "Username", flex: 1, minWidth: 150 },
|
||||
{ field: "password", headerName: "Password", flex: 1, minWidth: 150 },
|
||||
{
|
||||
field: "password",
|
||||
headerName: "Password",
|
||||
width: 175,
|
||||
renderHeader: () => (
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
Password
|
||||
<IconButton size="small" onClick={(e) => { e.stopPropagation(); togglePasswordVisibility(); }} sx={{ marginLeft: 1 }}>
|
||||
{isPasswordVisible ? <LockOpen /> : <Lock />}
|
||||
</IconButton>
|
||||
</div>
|
||||
),
|
||||
renderCell: (params: GridRenderCellParams<EmailDomain>) =>
|
||||
isPasswordVisible ? params.value : "••••••••",
|
||||
},
|
||||
{ field: "displayOrder", headerName: "Display Order", width: 120 },
|
||||
{ field: "isActive", headerName: "Active", width: 75 },
|
||||
];
|
||||
|
||||
const togglePasswordVisibility = async () => {
|
||||
if (isPasswordVisible) {
|
||||
setIsPasswordVisible(false);
|
||||
setEmailDomainsWithPasswords(null); // Revert to setupData.emailDomains (masked)
|
||||
} else {
|
||||
try {
|
||||
setIsPasswordVisible(true);
|
||||
await loadEmailDomainsWithPasswords();
|
||||
} catch (error) {
|
||||
console.error("Error fetching email domains:", error);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loadEmailDomainsWithPasswords = async () => {
|
||||
const response = await fetch("/api/emailDomains/GetAll?activeOnly=false&returnPassword=true");
|
||||
const data = await response.json();
|
||||
setEmailDomainsWithPasswords(data);
|
||||
}
|
||||
const handleNew = () => {
|
||||
setSelectedRow(null);
|
||||
setOpen(true);
|
||||
@ -45,9 +80,27 @@ function EmailDomains() {
|
||||
setOpen(true);
|
||||
};
|
||||
|
||||
const handleUpdateRow = (updatedRow: EmailDomain) => {
|
||||
setupData.setEmailDomains(updatedRow);
|
||||
const handleSaveRow = (savedRow: EmailDomain, password: string) => {
|
||||
setupData.setEmailDomains(savedRow);
|
||||
if (isPasswordVisible) {
|
||||
if (password)
|
||||
updateEmailDomains({ ...savedRow, password: password }); // Update local state
|
||||
else
|
||||
updateEmailDomains({ ...savedRow });
|
||||
}
|
||||
};
|
||||
const updateEmailDomains = (updatedEmailDomain: EmailDomain) => {
|
||||
setEmailDomainsWithPasswords((prev) => {
|
||||
if (prev == null) return null;
|
||||
|
||||
const exists = prev.some((e) => e.id === updatedEmailDomain.id);
|
||||
|
||||
return exists
|
||||
? prev.map((domain) => (domain.id === updatedEmailDomain.id ? updatedEmailDomain : domain))
|
||||
: [...prev, updatedEmailDomain];
|
||||
});
|
||||
};
|
||||
const displayRows = isPasswordVisible ? (emailDomainsWithPasswords ?? setupData.emailDomains) : setupData.emailDomains;
|
||||
|
||||
return (
|
||||
<Box ref={gridContainerRef} sx={{
|
||||
@ -59,29 +112,38 @@ function EmailDomains() {
|
||||
}}>
|
||||
<Box sx={{ position: 'absolute', inset: 0 }}>
|
||||
{isMobile ? (
|
||||
<List>
|
||||
{setupData.emailDomains.map((row) => (
|
||||
<Card key={row.id} sx={{ marginBottom: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6">{row.name}</Typography>
|
||||
<Typography variant="body2">ID: {row.id}</Typography>
|
||||
<Typography variant="body2">Email: {row.emailAddress}</Typography>
|
||||
<Typography variant="body2">Username: {row.username}</Typography>
|
||||
<Typography variant="body2">Password: {row.password}</Typography>
|
||||
<Typography variant="body2">Display Order: {row.displayOrder}</Typography>
|
||||
<Typography variant="body2">Active: {row.isActive ? "Yes" : "No"}</Typography>
|
||||
</CardContent>
|
||||
<IconButton onClick={(e) => { e.stopPropagation(); handleEdit(row); }}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Card>
|
||||
))}
|
||||
</List>
|
||||
<><Box sx={{ display: "flex", justifyContent: "flex-end", marginBottom: 2 }}>
|
||||
<Button
|
||||
onClick={togglePasswordVisibility}
|
||||
startIcon={isPasswordVisible ? <LockOpen /> : <Lock />}
|
||||
>
|
||||
{isPasswordVisible ? "Hide Passwords" : "Show Passwords"}
|
||||
</Button>
|
||||
</Box>
|
||||
<List>
|
||||
{displayRows.map((row) => (
|
||||
<Card key={row.id} sx={{ marginBottom: 2 }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<CardContent>
|
||||
<Typography variant="h6">{row.name}</Typography>
|
||||
<Typography variant="body2">ID: {row.id}</Typography>
|
||||
<Typography variant="body2">Email: {row.emailAddress}</Typography>
|
||||
<Typography variant="body2">Username: {row.username}</Typography>
|
||||
<Typography variant="body2">Password: {row.password}</Typography>
|
||||
<Typography variant="body2">Display Order: {row.displayOrder}</Typography>
|
||||
<Typography variant="body2">Active: {row.isActive ? "Yes" : "No"}</Typography>
|
||||
</CardContent>
|
||||
<IconButton onClick={(e) => { e.stopPropagation(); handleEdit(row); }}>
|
||||
<EditIcon />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</Card>
|
||||
))}
|
||||
</List>
|
||||
</>
|
||||
) : (
|
||||
<DataGrid
|
||||
rows={setupData.emailDomains}
|
||||
rows={displayRows}
|
||||
columns={columns}
|
||||
autoPageSize
|
||||
sx={{ minWidth: "600px" }}
|
||||
@ -94,12 +156,16 @@ function EmailDomains() {
|
||||
onClick={() => handleNew()}
|
||||
sx={{ marginRight: 2 }}
|
||||
>
|
||||
{setupData.emailDomainsLoading ? <CircularProgress size={24} color="inherit" /> : "Add New"}
|
||||
Add New
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={() => setupData.reloadEmailDomains()}
|
||||
onClick={() => {
|
||||
if (isPasswordVisible)
|
||||
loadEmailDomainsWithPasswords();
|
||||
setupData.reloadEmailDomains();
|
||||
}}
|
||||
sx={{ marginRight: 2 }}
|
||||
>
|
||||
{setupData.emailDomainsLoading ? <CircularProgress size={24} color="inherit" /> : "Refresh"}
|
||||
@ -133,7 +199,7 @@ function EmailDomains() {
|
||||
open={open}
|
||||
emailDomain={selectedRow}
|
||||
onClose={() => setOpen(false)}
|
||||
onSave={handleUpdateRow}
|
||||
onSave={handleSaveRow}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
@ -27,12 +27,19 @@ function Templates() {
|
||||
),
|
||||
},
|
||||
{ field: "id", headerName: "ID", width: 60 },
|
||||
{ field: "name", headerName: "Name", flex: 1, minWidth: 160 },
|
||||
{ field: "domainId", headerName: "Domain ID", width: 100 },
|
||||
{ field: "description", headerName: "Description", flex: 1, minWidth: 200 },
|
||||
{ field: "domainId", headerName: "Domain", maxWidth: 200 },
|
||||
{ field: "name", headerName: "Name", flex: 1, minWidth: 160, maxWidth:600 },
|
||||
{ field: "description", headerName: "Description", flex: 1, minWidth: 200, maxWidth: 600 },
|
||||
{ field: "isActive", headerName: "Active", width: 75 },
|
||||
];
|
||||
|
||||
const getMaxWidth = () => {
|
||||
let maxWidth = 0;
|
||||
for (const column of columns) {
|
||||
maxWidth += column.width ?? column.maxWidth ?? 0;
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
const handleNew = () => {
|
||||
setSelectedRow(null);
|
||||
setOpen(true);
|
||||
@ -80,7 +87,8 @@ function Templates() {
|
||||
rows={setupData.templates}
|
||||
columns={columns}
|
||||
autoPageSize
|
||||
sx={{ minWidth: "600px" }}
|
||||
sx={{
|
||||
minWidth: "600px", maxWidth: getMaxWidth() }}
|
||||
slots={{
|
||||
toolbar: () => (
|
||||
<GridToolbarContainer sx={{ display: "flex", alignItems: "center" }}>
|
||||
@ -90,7 +98,7 @@ function Templates() {
|
||||
onClick={() => handleNew()}
|
||||
sx={{ marginRight: 2 }}
|
||||
>
|
||||
{setupData.templatesLoading ? <CircularProgress size={24} color="inherit" /> : "Add New"}
|
||||
{"Add New"}
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user