diff --git a/package-lock.json b/package-lock.json
index 155b8c3..64f1928 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2631,9 +2631,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-alignment/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -3144,9 +3144,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-autosave/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -4126,9 +4126,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-bookmark/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -7241,9 +7241,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-clipboard/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -7758,9 +7758,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-code-block/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -8284,9 +8284,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-editor-balloon/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -8800,9 +8800,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-editor-decoupled/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -8903,9 +8903,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-editor-inline/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9006,9 +9006,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-editor-multi-root/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9125,9 +9125,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-emoji/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9235,9 +9235,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-enter/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9750,9 +9750,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-find-and-replace/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9853,9 +9853,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-font/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -9972,9 +9972,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-fullscreen/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -10486,9 +10486,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-highlight/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -10589,9 +10589,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-horizontal-line/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -10692,9 +10692,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-html-embed/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -10902,9 +10902,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-html-support/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -11848,9 +11848,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-language/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -12790,9 +12790,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-markdown-gfm/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -13319,9 +13319,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-mention/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -13421,9 +13421,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-minimap/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -13524,9 +13524,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-page-break/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14062,9 +14062,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-remove-format/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14165,9 +14165,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-restricted-editing/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14267,9 +14267,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-select-all/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14369,9 +14369,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-show-blocks/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14472,9 +14472,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-source-editing/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14588,9 +14588,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-special-characters/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -14744,9 +14744,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-style/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15256,9 +15256,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-theme-lark/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15384,9 +15384,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-undo/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15483,9 +15483,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-upload/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15594,9 +15594,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-watchdog/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15712,9 +15712,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-widget/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -15814,9 +15814,9 @@
}
},
"node_modules/@ckeditor/ckeditor5-word-count/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -19654,9 +19654,9 @@
}
},
"node_modules/@types/react-dom": {
- "version": "19.2.2",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz",
- "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
+ "version": "19.2.3",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz",
+ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
"license": "MIT",
"peer": true,
"peerDependencies": {
@@ -22104,9 +22104,9 @@
}
},
"node_modules/ckeditor5/node_modules/color-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz",
- "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==",
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz",
+ "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==",
"license": "MIT",
"peer": true,
"engines": {
@@ -29007,9 +29007,9 @@
}
},
"node_modules/mdast-util-to-hast": {
- "version": "13.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
- "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
"license": "MIT",
"peer": true,
"dependencies": {
@@ -31977,6 +31977,23 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
+ "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+ "license": "MIT",
+ "optional": true,
+ "peer": true,
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
diff --git a/src/PagesAdm/gestao.css b/src/PagesAdm/gestao.css
index 5991041..9951eae 100644
--- a/src/PagesAdm/gestao.css
+++ b/src/PagesAdm/gestao.css
@@ -1,4 +1,3 @@
-
.dashboard-container {
padding: 2rem;
font-family: 'Arial', sans-serif;
@@ -6,7 +5,6 @@
min-height: 100vh;
}
-
.dashboard-header {
display: flex;
justify-content: space-between;
@@ -34,143 +32,133 @@
border: none;
border-radius: 8px;
font-size: 1rem;
+ font-weight: 600;
cursor: pointer;
- transition: background-color 0.3s, transform 0.25s ease, box-shadow 0.25s ease;
+ transition: all 0.3s ease;
+ box-shadow: 0 2px 8px rgba(30, 58, 138, 0.3);
}
.new-user-btn:hover {
background-color: #162d6b;
transform: translateY(-2px);
- box-shadow: 0px 4px 12px rgba(30, 58, 138, 0.3);
+ box-shadow: 0 4px 12px rgba(30, 58, 138, 0.4);
}
-
.filters-container {
background: #fff;
border-radius: 12px;
- padding: 1.5rem;
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ padding: 1.2rem;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 2rem;
- transition: transform 0.2s ease, box-shadow 0.2s ease;
-}
-
-.filters-container:hover {
- transform: translateY(-3px);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
}
.filters-title {
- font-size: 18px;
+ font-size: 16px;
font-weight: bold;
margin-bottom: 0.3rem;
color: #333;
}
.filters-subtitle {
- font-size: 0.9rem;
+ font-size: 0.85rem;
color: #666;
margin-bottom: 1rem;
}
.filters-content {
display: flex;
- gap: 1rem;
+ gap: 0.8rem;
align-items: center;
}
.filters-input {
flex: 1;
- padding: 0.6rem 1rem;
+ padding: 0.5rem 0.8rem;
border: 1px solid #d1d5db;
- border-radius: 8px;
- font-size: 0.95rem;
+ border-radius: 6px;
+ font-size: 0.9rem;
color: #333;
- transition: border-color 0.2s, box-shadow 0.2s;
+ min-width: 200px;
+ transition: all 0.2s ease;
}
.filters-input:focus {
border-color: #1e3a8a;
- box-shadow: 0px 0px 0px 3px rgba(30, 58, 138, 0.2);
+ box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.1);
outline: none;
}
.filters-select {
- padding: 0.6rem 1rem;
+ padding: 0.5rem 0.8rem;
border: 1px solid #d1d5db;
- border-radius: 8px;
- font-size: 0.95rem;
+ border-radius: 6px;
+ font-size: 0.9rem;
background: #fff;
color: #333;
cursor: pointer;
- transition: border-color 0.2s, box-shadow 0.2s;
+ min-width: 140px;
+ transition: all 0.2s ease;
}
.filters-select:focus {
border-color: #1e3a8a;
- box-shadow: 0px 0px 0px 3px rgba(30, 58, 138, 0.2);
+ box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.1);
outline: none;
}
-
.cards-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 1.5rem;
+ gap: 1.2rem;
margin-bottom: 2rem;
}
.card {
background-color: white;
- padding: 1.5rem;
- border-radius: 12px;
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
+ padding: 1.2rem;
+ border-radius: 10px;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
border: 1px solid transparent;
- transition: transform 0.25s ease, box-shadow 0.25s ease, border 0.25s ease, background 0.25s ease;
+ transition: all 0.25s ease;
cursor: pointer;
}
.highlight:hover {
- transform: translateY(-6px);
- box-shadow: 0 8px 20px rgba(30, 58, 138, 0.2);
+ transform: translateY(-4px);
+ box-shadow: 0 6px 16px rgba(30, 58, 138, 0.2);
background: #f8faff;
border: 1px solid #1e3a8a33;
}
.card-label {
- font-size: 0.9rem;
+ font-size: 0.85rem;
color: #999;
margin-bottom: 0.5rem;
}
.card-value {
- font-size: 1.8rem;
+ font-size: 1.6rem;
font-weight: bold;
margin: 0;
color: #333;
}
.card-extra {
- font-size: 0.85rem;
+ font-size: 0.8rem;
color: #666;
}
.card-extra.positive {
color: #1e3a8a;
+ font-weight: 600;
}
-
.user-table-container {
background: #fff;
border-radius: 12px;
- padding: 1.5rem;
- box-shadow: 0 4px 6px rgba(0,0,0,0.1);
+ padding: 1.2rem;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-top: 2rem;
- transition: transform 0.25s ease, box-shadow 0.25s ease;
-}
-
-.user-table-container:hover {
- transform: translateY(-4px);
- box-shadow: 0 6px 14px rgba(0,0,0,0.15);
}
.user-table-container h2 {
@@ -193,7 +181,7 @@
.user-table th,
.user-table td {
- padding: 12px 15px;
+ padding: 10px 12px;
text-align: left;
border-bottom: 1px solid #e0e0e0;
}
@@ -202,10 +190,11 @@
background-color: #f3f4f6;
color: #333;
font-weight: 600;
+ font-size: 0.9rem;
}
.user-table tr {
- transition: background-color 0.25s ease;
+ transition: background-color 0.2s ease;
}
.user-table tr:hover {
@@ -214,44 +203,115 @@
.profile-badge {
background-color: #1e3a8a;
- color: #f7f7f7;
- padding: 3px 8px;
- border-radius: 8px;
- font-size: 0.85rem;
+ color: white;
+ padding: 4px 10px;
+ border-radius: 6px;
+ font-size: 0.8rem;
+ font-weight: 500;
display: inline-block;
}
.status-badge {
- padding: 3px 8px;
- border-radius: 8px;
- font-size: 0.85rem;
+ padding: 4px 10px;
+ border-radius: 6px;
+ font-size: 0.8rem;
color: #fff;
+ font-weight: 500;
display: inline-block;
text-transform: capitalize;
}
.status-badge.ativo {
- background-color: #28a745;
+ background-color: #1e3a8a;
}
.status-badge.inativo {
- background-color: #dc3545;
+ background-color: #6c757d;
}
.actions {
display: flex;
- gap: 10px;
+ gap: 8px;
+ flex-wrap: wrap;
}
-.action-icon {
+.action-btn {
+ border: none;
+ padding: 6px 12px;
+ font-size: 0.8rem;
+ font-weight: 500;
cursor: pointer;
- color: #555;
- transition: color 0.2s, transform 0.2s;
+ transition: all 0.2s ease;
+ border-radius: 4px;
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
}
-.action-icon:hover {
- color: #1e3a8a;
- transform: scale(1.2);
+.action-btn.detalhes {
+ background-color: #e6f2ff;
+ color: #004085;
+ border: 1px solid #b8d4ff;
+}
+
+.action-btn.detalhes:hover {
+ background-color: #cce4ff;
+ transform: translateY(-1px);
+}
+
+.action-btn.editar {
+ background-color: #fff3cd;
+ color: #856405;
+ border: 1px solid #ffeaa7;
+}
+
+.action-btn.editar:hover {
+ background-color: #ffeaa7;
+ transform: translateY(-1px);
+}
+
+.action-btn.excluir {
+ background-color: #f8d7da;
+ color: #721c24;
+ border: 1px solid #f1b0b7;
+}
+
+.action-btn.excluir:hover {
+ background-color: #f1b0b7;
+ transform: translateY(-1px);
+}
+
+.save-btn {
+ background-color: #1e3a8a;
+ color: white;
+ border: none;
+ padding: 8px 16px;
+ border-radius: 6px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.save-btn:hover {
+ background-color: #162d6b;
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(30, 58, 138, 0.3);
+}
+
+.edit-btn {
+ background-color: #fff3cd;
+ color: #856405;
+ border: 1px solid #ffeaa7;
+ padding: 8px 16px;
+ border-radius: 6px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.edit-btn:hover {
+ background-color: #ffeaa7;
+ transform: translateY(-1px);
}
html[data-bs-theme="dark"] .dashboard-container {
@@ -266,18 +326,17 @@ html[data-bs-theme="dark"] .dashboard-subtitle {
}
html[data-bs-theme="dark"] .new-user-btn {
- background-color: #2563eb;
- color: #fff;
+ background-color: #1e3a8a;
}
html[data-bs-theme="dark"] .new-user-btn:hover {
- background-color: #1e40af;
+ background-color: #162d6b;
}
html[data-bs-theme="dark"] .filters-container,
html[data-bs-theme="dark"] .user-table-container {
background: #1a1a1a;
- box-shadow: 0 4px 6px rgba(0,0,0,0.4);
+ box-shadow: 0 2px 8px rgba(0,0,0,0.4);
}
html[data-bs-theme="dark"] .filters-title,
@@ -299,19 +358,19 @@ html[data-bs-theme="dark"] .filters-select {
html[data-bs-theme="dark"] .filters-input:focus,
html[data-bs-theme="dark"] .filters-select:focus {
- border-color: #2563eb;
- box-shadow: 0px 0px 0px 3px rgba(37, 99, 235, 0.2);
+ border-color: #1e3a8a;
+ box-shadow: 0 0 0 2px rgba(30, 58, 138, 0.2);
}
html[data-bs-theme="dark"] .cards-container .card {
background-color: #181818;
color: #e0e0e0;
- box-shadow: 0 4px 6px rgba(0,0,0,0.4);
+ box-shadow: 0 2px 6px rgba(0,0,0,0.4);
}
html[data-bs-theme="dark"] .highlight:hover {
- background: #232a3a;
- border: 1px solid #2563eb33;
+ background: #1a1f2e;
+ border: 1px solid #1e3a8a33;
}
html[data-bs-theme="dark"] .card-label {
@@ -327,7 +386,7 @@ html[data-bs-theme="dark"] .card-extra {
}
html[data-bs-theme="dark"] .card-extra.positive {
- color: #2563eb;
+ color: #1e3a8a;
}
html[data-bs-theme="dark"] .user-table th {
@@ -341,26 +400,39 @@ html[data-bs-theme="dark"] .user-table td {
}
html[data-bs-theme="dark"] .user-table tr:hover {
- background-color: #232a3a;
+ background-color: #1a1f2e;
}
html[data-bs-theme="dark"] .profile-badge {
- background-color: #2563eb;
- color: #fff;
+ background-color: #1e3a8a;
}
-html[data-bs-theme="dark"] .status-badge.ativo {
- background-color: #28a745;
+html[data-bs-theme="dark"] .action-btn.detalhes {
+ background-color: #e6f2ff;
+ color: #004085;
+ border: 1px solid #b8d4ff;
}
-html[data-bs-theme="dark"] .status-badge.inativo {
- background-color: #dc3545;
+html[data-bs-theme="dark"] .action-btn.detalhes:hover {
+ background-color: #cce4ff;
}
-html[data-bs-theme="dark"] .action-icon {
- color: #bdbdbd;
+html[data-bs-theme="dark"] .action-btn.editar {
+ background-color: #fff3cd;
+ color: #856405;
+ border: 1px solid #ffeaa7;
}
-html[data-bs-theme="dark"] .action-icon:hover {
- color: #2563eb;
+html[data-bs-theme="dark"] .action-btn.editar:hover {
+ background-color: #ffeaa7;
+}
+
+html[data-bs-theme="dark"] .action-btn.excluir {
+ background-color: #f8d7da;
+ color: #721c24;
+ border: 1px solid #f1b0b7;
+}
+
+html[data-bs-theme="dark"] .action-btn.excluir:hover {
+ background-color: #f1b0b7;
}
\ No newline at end of file
diff --git a/src/PagesAdm/gestao.jsx b/src/PagesAdm/gestao.jsx
index 7d982b9..a5ecd88 100644
--- a/src/PagesAdm/gestao.jsx
+++ b/src/PagesAdm/gestao.jsx
@@ -1,14 +1,9 @@
-
import React from "react";
import "./gestao.css";
-import { FaEdit, FaTrash } from "react-icons/fa";
-
function UserDashboard() {
return (
-
-
-
+
Gestão de Usuários
@@ -91,8 +86,9 @@ function UserDashboard() {
Ativo |
20/12/2024, 08:30 |
-
-
+
+
+
|
@@ -103,8 +99,9 @@ function UserDashboard() {
| Ativo |
19/12/2024, 14:20 |
-
-
+
+
+
|
@@ -115,8 +112,9 @@ function UserDashboard() {
| Ativo |
20/12/2024, 07:45 |
-
-
+
+
+
|
@@ -127,8 +125,9 @@ function UserDashboard() {
| Inativo |
15/12/2024, 16:30 |
-
-
+
+
+
|
@@ -138,5 +137,4 @@ function UserDashboard() {
);
}
-
export default UserDashboard;
\ No newline at end of file
diff --git a/src/PagesMedico/DoctorAgendamentoManager.jsx b/src/PagesMedico/DoctorAgendamentoManager.jsx
index acee929..567646c 100644
--- a/src/PagesMedico/DoctorAgendamentoManager.jsx
+++ b/src/PagesMedico/DoctorAgendamentoManager.jsx
@@ -1,785 +1,1034 @@
-import React, { useState, useMemo, useEffect, useCallback } from 'react';
-import { useNavigate } from 'react-router-dom';
-import API_KEY from '../components/utils/apiKeys.js';
-import AgendamentoCadastroManager from '../pages/AgendamentoCadastroManager.jsx';
-import { GetAllDoctors } from '../components/utils/Functions-Endpoints/Doctor.js';
-import { useAuth } from '../components/utils/AuthProvider.js';
-import dayjs from 'dayjs';
-import 'dayjs/locale/pt-br';
-import isBetween from 'dayjs/plugin/isBetween';
-import localeData from 'dayjs/plugin/localeData';
-import { Search, ChevronLeft, ChevronRight, Edit, Trash2, CheckCircle } from 'lucide-react';
+import React, { useState, useMemo, useEffect, useCallback } from "react";
+import { useNavigate } from "react-router-dom";
+import API_KEY from "../components/utils/apiKeys.js";
+import AgendamentoCadastroManager from "../pages/AgendamentoCadastroManager.jsx";
+import { GetAllDoctors } from "../components/utils/Functions-Endpoints/Doctor.js";
+import { useAuth } from "../components/utils/AuthProvider.js";
+import dayjs from "dayjs";
+import "dayjs/locale/pt-br";
+import isBetween from "dayjs/plugin/isBetween";
+import localeData from "dayjs/plugin/localeData";
+import {
+ Search,
+ ChevronLeft,
+ ChevronRight,
+ Edit,
+ Trash2,
+ CheckCircle,
+} from "lucide-react";
import "../pages/style/Agendamento.css";
-import '../pages/style/FilaEspera.css';
-import Spinner from '../components/Spinner.jsx';
+import "../pages/style/FilaEspera.css";
+import Spinner from "../components/Spinner.jsx";
-
-dayjs.locale('pt-br');
+dayjs.locale("pt-br");
dayjs.extend(isBetween);
dayjs.extend(localeData);
-
const Agendamento = () => {
- const [searchTerm, setSearchTerm] = useState('');
- const navigate = useNavigate();
- const { getAuthorizationHeader, user } = useAuth();
- const authHeader = getAuthorizationHeader();
+ const navigate = useNavigate();
+ const { getAuthorizationHeader, user } = useAuth();
+ const authHeader = getAuthorizationHeader();
- const ID_MEDICO_ESPECIFICO = "078d2a67-b4c1-43c8-ae32-c1e75bb5b3df";
+ const ID_MEDICO_ESPECIFICO = "078d2a67-b4c1-43c8-ae32-c1e75bb5b3df";
- const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]);
- const [selectedID, setSelectedId] = useState('0');
- const [filaEsperaData, setFilaEsperaData] = useState([]);
- const [FiladeEspera, setFiladeEspera] = useState(false);
- const [PageNovaConsulta, setPageConsulta] = useState(false);
- const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState({});
- const [showDeleteModal, setShowDeleteModal] = useState(false);
- const [ListaDeMedicos, setListaDeMedicos] = useState([]);
- const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]);
- const [searchTermDoctor, setSearchTermDoctor] = useState('');
- const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" });
- const [motivoCancelamento, setMotivoCancelamento] = useState("");
- const [showSpinner, setShowSpinner] = useState(true);
- const [waitlistSearch, setWaitlistSearch] = useState('');
- const [waitSortKey, setWaitSortKey] = useState(null);
- const [waitSortDir, setWaitSortDir] = useState('asc');
- const [waitPage, setWaitPage] = useState(1);
- const [waitPerPage, setWaitPerPage] = useState(10);
- const [cacheMedicos, setCacheMedicos] = useState({});
- const [cachePacientes, setCachePacientes] = useState({});
- const [currentDate, setCurrentDate] = useState(dayjs());
- const [selectedDay, setSelectedDay] = useState(dayjs());
- const [agendamentoParaEdicao, setAgendamentoParaEdicao] = useState(null);
- const [quickJump, setQuickJump] = useState({
-
- month: currentDate.month(),
- year: currentDate.year()
- });
+ const [listaTodosAgendamentos, setListaTodosAgendamentos] = useState([]);
+ const [selectedID, setSelectedId] = useState("0");
+ const [filaEsperaData, setFilaEsperaData] = useState([]);
+ const [FiladeEspera, setFiladeEspera] = useState(false);
+ const [PageNovaConsulta, setPageConsulta] = useState(false);
+ const [DictAgendamentosOrganizados, setAgendamentosOrganizados] = useState(
+ {}
+ );
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+ const [ListaDeMedicos, setListaDeMedicos] = useState([]);
+ const [FiltredTodosMedicos, setFiltredTodosMedicos] = useState([]);
+ const [searchTermDoctor, setSearchTermDoctor] = useState("");
+ const [MedicoFiltrado, setMedicoFiltrado] = useState({ id: "vazio" });
+ const [motivoCancelamento, setMotivoCancelamento] = useState("");
+ const [showSpinner, setShowSpinner] = useState(true);
+ const [waitlistSearch, setWaitlistSearch] = useState("");
+ const [waitSortKey, setWaitSortKey] = useState(null);
+ const [waitSortDir, setWaitSortDir] = useState("asc");
+ const [waitPage, setWaitPage] = useState(1);
+ const [waitPerPage, setWaitPerPage] = useState(10);
+ const [cacheMedicos, setCacheMedicos] = useState({});
+ const [cachePacientes, setCachePacientes] = useState({});
+ const [currentDate, setCurrentDate] = useState(dayjs());
+ const [selectedDay, setSelectedDay] = useState(dayjs());
+ const [agendamentoParaEdicao, setAgendamentoParaEdicao] = useState(null);
+ const [quickJump, setQuickJump] = useState({
+ month: currentDate.month(),
+ year: currentDate.year(),
+ });
- const fetchAppointments = useCallback(async () => {
- if (!authHeader) return;
- setShowSpinner(true);
- const myHeaders = new Headers();
- myHeaders.append("Authorization", authHeader);
- myHeaders.append("apikey", API_KEY);
- const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' };
+ const fetchAppointments = useCallback(async () => {
+ if (!authHeader) return;
+ setShowSpinner(true);
+ const myHeaders = new Headers();
+ myHeaders.append("Authorization", authHeader);
+ myHeaders.append("apikey", API_KEY);
+ const requestOptions = {
+ method: "GET",
+ headers: myHeaders,
+ redirect: "follow",
+ };
- const apiUrl = `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${ID_MEDICO_ESPECIFICO}&select=*`;
+ const apiUrl = `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?doctor_id=eq.${ID_MEDICO_ESPECIFICO}&select=*`;
- try {
- const res = await fetch(apiUrl, requestOptions);
- const data = await res.json();
- setListaTodosAgendamentos(data || []);
- } catch (err) {
- console.error('Erro ao buscar agendamentos', err);
- setListaTodosAgendamentos([]);
- } finally {
- setShowSpinner(false);
- }
- }, [authHeader, ID_MEDICO_ESPECIFICO]);
+ try {
+ const res = await fetch(apiUrl, requestOptions);
+ const data = await res.json();
+ setListaTodosAgendamentos(data || []);
+ } catch (err) {
+ console.error("Erro ao buscar agendamentos", err);
+ setListaTodosAgendamentos([]);
+ } finally {
+ setShowSpinner(false);
+ }
+ }, [authHeader, ID_MEDICO_ESPECIFICO]);
-
- const updateAppointmentStatus = useCallback(async (id, updates) => {
- setShowSpinner(true);
- const myHeaders = new Headers();
- myHeaders.append("Authorization", authHeader);
- myHeaders.append("apikey", API_KEY);
- myHeaders.append("Content-Type", "application/json");
- myHeaders.append("Prefer", "return=representation");
- const requestOptions = { method: 'PATCH', headers: myHeaders, body: JSON.stringify(updates) };
- try {
- const response = await fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`, requestOptions);
- if (response.ok) {
- await fetchAppointments();
- return true;
- } else {
- console.error('Erro ao atualizar agendamento:', await response.text());
- return false;
- }
- } catch (error) {
- console.error('Erro de rede/servidor:', error);
- return false;
- } finally {
- setShowSpinner(false);
- }
- }, [authHeader, fetchAppointments]);
-
-
- const deleteConsulta = useCallback(async (id) => {
- const success = await updateAppointmentStatus(id, { status: "cancelled", cancellation_reason: motivoCancelamento, updated_at: new Date().toISOString() });
- if (success) {
- setShowDeleteModal(false);
- setMotivoCancelamento("");
- setSelectedId('0');
+ const updateAppointmentStatus = useCallback(
+ async (id, updates) => {
+ setShowSpinner(true);
+ const myHeaders = new Headers();
+ myHeaders.append("Authorization", authHeader);
+ myHeaders.append("apikey", API_KEY);
+ myHeaders.append("Content-Type", "application/json");
+ myHeaders.append("Prefer", "return=representation");
+ const requestOptions = {
+ method: "PATCH",
+ headers: myHeaders,
+ body: JSON.stringify(updates),
+ };
+ try {
+ const response = await fetch(
+ `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/appointments?id=eq.${id}`,
+ requestOptions
+ );
+ if (response.ok) {
+ await fetchAppointments();
+ return true;
} else {
- alert("Falha ao cancelar a consulta.");
+ console.error(
+ "Erro ao atualizar agendamento:",
+ await response.text()
+ );
+ return false;
}
- }, [motivoCancelamento, updateAppointmentStatus]);
+ } catch (error) {
+ console.error("Erro de rede/servidor:", error);
+ return false;
+ } finally {
+ setShowSpinner(false);
+ }
+ },
+ [authHeader, fetchAppointments]
+ );
- const confirmConsulta = useCallback(async (id) => {
- const success = await updateAppointmentStatus(id, { status: "agendado", cancellation_reason: null, updated_at: new Date().toISOString() });
- if (success) {
- setSelectedId('0');
- } else {
- alert("Falha ao reverter o cancelamento.");
+ const deleteConsulta = useCallback(
+ async (id) => {
+ const success = await updateAppointmentStatus(id, {
+ status: "cancelled",
+ cancellation_reason: motivoCancelamento,
+ updated_at: new Date().toISOString(),
+ });
+ if (success) {
+ setShowDeleteModal(false);
+ setMotivoCancelamento("");
+ setSelectedId("0");
+ } else {
+ alert("Falha ao cancelar a consulta.");
+ }
+ },
+ [motivoCancelamento, updateAppointmentStatus]
+ );
+
+ const confirmConsulta = useCallback(
+ async (id) => {
+ const success = await updateAppointmentStatus(id, {
+ status: "agendado",
+ cancellation_reason: null,
+ updated_at: new Date().toISOString(),
+ });
+ if (success) {
+ setSelectedId("0");
+ } else {
+ alert("Falha ao reverter o cancelamento.");
+ }
+ },
+ [updateAppointmentStatus]
+ );
+
+ useEffect(() => {
+ if (authHeader) {
+ fetchAppointments();
+
+ if (user?.role !== "doctor") {
+ GetAllDoctors(authHeader).then((docs) => {
+ if (docs) {
+ setListaDeMedicos(
+ docs.map((d) => ({ nomeMedico: d.full_name, idMedico: d.id }))
+ );
+ }
+ });
+ }
+ }
+ }, [authHeader, fetchAppointments, user?.role]);
+
+ useEffect(() => {
+ const processData = async () => {
+ if (!listaTodosAgendamentos.length) {
+ setAgendamentosOrganizados({});
+ setFilaEsperaData([]);
+ return;
+ }
+
+ setShowSpinner(true);
+
+ const appointmentsToShow = listaTodosAgendamentos;
+
+ const patientIdsToFetch = new Set();
+ const doctorIdsToFetch = new Set();
+
+ appointmentsToShow.forEach((ag) => {
+ if (ag.patient_id && !cachePacientes[ag.patient_id]) {
+ patientIdsToFetch.add(ag.patient_id);
}
- }, [updateAppointmentStatus]);
-
- useEffect(() => {
- if(authHeader) {
- fetchAppointments();
- if (user?.role !== 'doctor') {
- GetAllDoctors(authHeader).then(docs => {
- if (docs) {
- setListaDeMedicos(docs.map(d => ({ nomeMedico: d.full_name, idMedico: d.id })));
- }
- });
- }
+ if (ag.doctor_id && !cacheMedicos[ag.doctor_id]) {
+ doctorIdsToFetch.add(ag.doctor_id);
}
- }, [authHeader, fetchAppointments, user?.role]);
+ });
- useEffect(() => {
- const processData = async () => {
- if (!listaTodosAgendamentos.length) {
- setAgendamentosOrganizados({});
- setFilaEsperaData([]);
- return;
+ const fetchPromises = [];
+
+ if (patientIdsToFetch.size > 0) {
+ const query = `id=in.(${Array.from(patientIdsToFetch).join(",")})`;
+ fetchPromises.push(
+ fetch(
+ `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?${query}&select=*`,
+ {
+ headers: { apikey: API_KEY, Authorization: authHeader },
}
+ ).then((res) => res.json())
+ );
+ } else {
+ fetchPromises.push(Promise.resolve(null));
+ }
- setShowSpinner(true);
-
- const appointmentsToShow = listaTodosAgendamentos;
-
- const patientIdsToFetch = new Set();
- const doctorIdsToFetch = new Set();
-
- appointmentsToShow.forEach(ag => {
- if (ag.patient_id && !cachePacientes[ag.patient_id]) {
- patientIdsToFetch.add(ag.patient_id);
- }
- if (ag.doctor_id && !cacheMedicos[ag.doctor_id]) {
- doctorIdsToFetch.add(ag.doctor_id);
- }
- });
-
- const fetchPromises = [];
-
- if (patientIdsToFetch.size > 0) {
- const query = `id=in.(${Array.from(patientIdsToFetch).join(',')})`;
- fetchPromises.push(
- fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?${query}&select=*`, {
- headers: { apikey: API_KEY, Authorization: authHeader }
- }).then(res => res.json())
- );
- } else {
- fetchPromises.push(Promise.resolve(null));
+ if (doctorIdsToFetch.size > 0) {
+ const query = `id=in.(${Array.from(doctorIdsToFetch).join(",")})`;
+ fetchPromises.push(
+ fetch(
+ `https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?${query}&select=id,full_name`,
+ {
+ headers: { apikey: API_KEY, Authorization: authHeader },
}
+ ).then((res) => res.json())
+ );
+ } else {
+ fetchPromises.push(Promise.resolve(null));
+ }
- if (doctorIdsToFetch.size > 0) {
- const query = `id=in.(${Array.from(doctorIdsToFetch).join(',')})`;
- fetchPromises.push(
- fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?${query}&select=id,full_name`, {
- headers: { apikey: API_KEY, Authorization: authHeader }
- }).then(res => res.json())
- );
- } else {
- fetchPromises.push(Promise.resolve(null));
- }
-
- const [newPatients, newDoctors] = await Promise.all(fetchPromises);
+ const [newPatients, newDoctors] = await Promise.all(fetchPromises);
- const updatedPatientCache = { ...cachePacientes };
- if (newPatients) newPatients.forEach(p => updatedPatientCache[p.id] = p);
+ const updatedPatientCache = { ...cachePacientes };
+ if (newPatients)
+ newPatients.forEach((p) => (updatedPatientCache[p.id] = p));
- const updatedDoctorCache = { ...cacheMedicos };
- if (newDoctors) newDoctors.forEach(d => updatedDoctorCache[d.id] = d);
-
- setCachePacientes(updatedPatientCache);
- setCacheMedicos(updatedDoctorCache);
+ const updatedDoctorCache = { ...cacheMedicos };
+ if (newDoctors) newDoctors.forEach((d) => (updatedDoctorCache[d.id] = d));
- const newDict = {};
- const newFila = [];
+ setCachePacientes(updatedPatientCache);
+ setCacheMedicos(updatedDoctorCache);
- for (const agendamento of appointmentsToShow) {
- const medico = updatedDoctorCache[agendamento.doctor_id];
- const paciente = updatedPatientCache[agendamento.patient_id];
+ const newDict = {};
+ const newFila = [];
- if (!medico || !paciente) continue;
+ for (const agendamento of appointmentsToShow) {
+ const medico = updatedDoctorCache[agendamento.doctor_id];
+ const paciente = updatedPatientCache[agendamento.patient_id];
- const agendamentoMelhorado = {
- ...agendamento,
- medico_nome: medico.full_name || 'N/A',
- paciente_nome: paciente.full_name || 'N/A',
- paciente_cpf: paciente.cpf || 'N/A'
- };
+ if (!medico || !paciente) continue;
- if (agendamento.status === "requested") {
- newFila.push({ agendamento: agendamentoMelhorado, Infos: agendamentoMelhorado });
- } else {
- const DiaAgendamento = dayjs(agendamento.scheduled_at).format("YYYY-MM-DD");
- if (!newDict[DiaAgendamento]) newDict[DiaAgendamento] = [];
- newDict[DiaAgendamento].push(agendamentoMelhorado);
- }
- }
-
- for (const key in newDict) {
- newDict[key].sort((a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at));
- }
-
- setAgendamentosOrganizados(newDict);
- setFilaEsperaData(newFila);
- setShowSpinner(false);
+ const agendamentoMelhorado = {
+ ...agendamento,
+ medico_nome: medico.full_name || "N/A",
+ paciente_nome: paciente.full_name || "N/A",
+ paciente_cpf: paciente.cpf || "N/A",
};
- processData();
- }, [listaTodosAgendamentos, authHeader]);
-
- const handleEditConsulta = (agendamento) => {
- setAgendamentoParaEdicao(agendamento);
- setPageConsulta(true);
- };
-
- const handleSearchMedicos = (term) => {
- setSearchTermDoctor(term);
- if (term.trim()) {
- const filtered = ListaDeMedicos.filter(medico =>
- medico.nomeMedico.toLowerCase().includes(term.toLowerCase())
- );
- setFiltredTodosMedicos(filtered);
+ if (agendamento.status === "requested") {
+ newFila.push({
+ agendamento: agendamentoMelhorado,
+ Infos: agendamentoMelhorado,
+ });
} else {
- setFiltredTodosMedicos([]);
- setMedicoFiltrado({ id: "vazio" });
+ const DiaAgendamento = dayjs(agendamento.scheduled_at).format(
+ "YYYY-MM-DD"
+ );
+ if (!newDict[DiaAgendamento]) newDict[DiaAgendamento] = [];
+ newDict[DiaAgendamento].push(agendamentoMelhorado);
}
- };
-
- const generateDateGrid = () => {
- const grid = [];
- const startOfMonth = currentDate.startOf('month');
- let currentDay = startOfMonth.subtract(startOfMonth.day(), 'day');
- for (let i = 0; i < 42; i++) {
- grid.push(currentDay);
- currentDay = currentDay.add(1, 'day');
- }
- return grid;
- };
+ }
- const dateGrid = useMemo(() => generateDateGrid(), [currentDate]);
- const weekDays = ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'];
- const handleDateClick = (day) => setSelectedDay(day);
-
- const DeleteModal = () => (
-
-
-
-
-
Confirmação de Cancelamento
-
-
-
-
Qual o motivo do cancelamento? (Opcional)
-
-
-
-
-
-
-
-
-
- );
-
- useEffect(() => {
- setQuickJump({
- month: currentDate.month(),
- year: currentDate.year()
- });
- }, [currentDate]);
-
- const filaEsperaFiltrada = useMemo(() => {
- if (!waitlistSearch.trim()) return filaEsperaData;
- const term = waitlistSearch.toLowerCase();
- return filaEsperaData.filter(item =>
- (item?.Infos?.paciente_nome?.toLowerCase() || '').includes(term) ||
- (item?.Infos?.paciente_cpf?.toLowerCase() || '').includes(term) ||
- (item?.Infos?.medico_nome?.toLowerCase() || '').includes(term)
+ for (const key in newDict) {
+ newDict[key].sort(
+ (a, b) => new Date(a.scheduled_at) - new Date(b.scheduled_at)
);
- }, [waitlistSearch, filaEsperaData]);
+ }
- const applySortingWaitlist = useCallback((arr) => {
- if (!Array.isArray(arr) || !waitSortKey) return arr;
- const copy = [...arr];
- const key = waitSortKey;
- const dir = waitSortDir === 'asc' ? 1 : -1;
- copy.sort((a, b) => {
- const valA = key === 'data' ? new Date(a.agendamento.scheduled_at) : (a.Infos?.[`${key}_nome`] || '');
- const valB = key === 'data' ? new Date(b.agendamento.scheduled_at) : (b.Infos?.[`${key}_nome`] || '');
- if (valA < valB) return -1 * dir;
- if (valA > valB) return 1 * dir;
- return 0;
- });
- return copy;
- }, [waitSortKey, waitSortDir]);
-
- const filaEsperaOrdenada = useMemo(() => applySortingWaitlist(filaEsperaFiltrada), [filaEsperaFiltrada, applySortingWaitlist]);
- const waitTotalPages = Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1;
- const waitIndiceInicial = (waitPage - 1) * waitPerPage;
- const waitIndiceFinal = waitIndiceInicial + waitPerPage;
- const filaEsperaPaginada = filaEsperaOrdenada.slice(waitIndiceInicial, waitIndiceFinal);
-
- const gerarNumerosWaitPages = () => {
- const paginas = [];
- const paginasParaMostrar = 5;
- let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2));
- let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1);
- inicio = Math.max(1, fim - paginasParaMostrar + 1);
- for (let i = inicio; i <= fim; i++) paginas.push(i);
- return paginas;
+ setAgendamentosOrganizados(newDict);
+ setFilaEsperaData(newFila);
+ setShowSpinner(false);
};
- useEffect(() => { setWaitPage(1); }, [waitlistSearch, waitSortKey, waitSortDir]);
+ processData();
+ }, [listaTodosAgendamentos, authHeader]);
- const handleQuickJumpChange = (type, value) => {
- setQuickJump(prev => ({ ...prev, [type]: Number(value) }));
- };
+ const handleEditConsulta = (agendamento) => {
+ setAgendamentoParaEdicao(agendamento);
+ setPageConsulta(true);
+ };
- const applyQuickJump = () => {
- let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1);
- setCurrentDate(newDate);
- setSelectedDay(newDate);
- };
+ const handleSearchMedicos = (term) => {
+ setSearchTermDoctor(term);
+ if (term.trim()) {
+ const filtered = ListaDeMedicos.filter((medico) =>
+ medico.nomeMedico.toLowerCase().includes(term.toLowerCase())
+ );
+ setFiltredTodosMedicos(filtered);
+ } else {
+ setFiltredTodosMedicos([]);
+ setMedicoFiltrado({ id: "vazio" });
+ }
+ };
- return (
-
-
Agendar nova consulta
- {!PageNovaConsulta ? (
-
- {/* ABA + BOTÕES NA MESMA BARRA */}
-
-
-
-
-
+ const generateDateGrid = () => {
+ const grid = [];
+ const startOfMonth = currentDate.startOf("month");
+ let currentDay = startOfMonth.subtract(startOfMonth.day(), "day");
+ for (let i = 0; i < 42; i++) {
+ grid.push(currentDay);
+ currentDay = currentDay.add(1, "day");
+ }
+ return grid;
+ };
-
-
-
-
-
-
-
- {/* RESTO DO CONTEÚDO */}
-
- {!FiladeEspera ? (
-
-
-
- {selectedDay.format('MMM')}
- {selectedDay.format('DD')}
-
-
-
{selectedDay.format('dddd')}
-
{selectedDay.format('D [de] MMMM [de] YYYY')}
-
-
-
- Realizado
-
-
- Confirmado
-
-
- Agendado
-
-
- Cancelado
-
-
-
-
-
Consultas para {selectedDay.format('DD/MM')}
- {showSpinner ? (
-
- ) : (
- DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')] &&
- DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]
- .filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id)
- .length > 0 ? (
- DictAgendamentosOrganizados[selectedDay.format('YYYY-MM-DD')]
- .filter(app => MedicoFiltrado.id === "vazio" || app.doctor_id === MedicoFiltrado.id)
- .map(app => (
-
-
- {dayjs(app.scheduled_at).format('HH:mm')}
-
-
- {app.paciente_nome}
- Dr(a). {app.medico_nome}
-
-
- {app.status === 'cancelled' ? (
-
- ) : (
-
- )}
- {app.status !== 'cancelled' && (
-
- )}
-
-
- ))
- ) : (
-
-
Nenhuma consulta agendada.
-
- )
- )}
-
-
-
-
-{/* FILTRO POR PACIENTE DENTRO DO CALENDÁRIO */}
-
-
- Filtrar por Paciente
-
- setSearchTerm(e.target.value)}
- />
-
- Buscar paciente para filtrar consultas
-
-
-
-
-
{currentDate.format('MMMM [de] YYYY')}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {weekDays.map(day => (
-
{day}
- ))}
- {dateGrid.map((day, index) => {
- const dayString = day.format('YYYY-MM-DD');
- const appointmentsOnDay = DictAgendamentosOrganizados[dayString] || [];
-const filteredAppointments = appointmentsOnDay.filter(app =>
- !searchTerm
- ? true
- : (app.paciente_nome || '').toLowerCase().includes(searchTerm.toLowerCase())
-);
- const cellClasses = `day-cell ${
- day.isSame(currentDate, 'month') ? 'current-month' : 'other-month'
- } ${day.isSame(dayjs(), 'day') ? 'today' : ''} ${
- day.isSame(selectedDay, 'day') ? 'selected' : ''
- }`;
- return (
-
handleDateClick(day)}
- >
-
{day.format('D')}
- {filteredAppointments.length > 0 && (
-
- {filteredAppointments.length}
-
-)}
-
- );
- })}
-
-
-
- ) : (
-
-
-
-
-
-
Fila de Espera
-
-
-
-
- Filtros
-
-
- setWaitlistSearch(e.target.value)}
- />
-
- Digite o nome do paciente, CPF ou nome do médico
-
-
-
-
- Ordenar por:
-
-
-
-
-
- {filaEsperaFiltrada.length} DE {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
-
-
-
-
-
-
-
-
- | Nome do Paciente |
- CPF |
- Médico Solicitado |
- Data da Solicitação |
- Ações |
-
-
-
- {filaEsperaPaginada.length > 0 ? (
- filaEsperaPaginada.map((item, index) => (
-
- | {item?.Infos?.paciente_nome} |
- {item?.Infos?.paciente_cpf} |
- {item?.Infos?.medico_nome} |
- {dayjs(item.agendamento.scheduled_at).format('DD/MM/YYYY')} |
-
-
- |
-
- ))
- ) : (
-
-
-
- {showSpinner ? (
-
- ) : (
- <>
-
- Nenhuma solicitação encontrada.
- >
- )}
-
- |
-
- )}
-
-
-
- {filaEsperaFiltrada.length > 0 && (
-
-
- Itens por página:
-
-
-
-
- Página {waitPage} de {waitTotalPages} • Mostrando {waitIndiceInicial + 1}-{Math.min(waitIndiceFinal, filaEsperaFiltrada.length)} de {filaEsperaFiltrada.length}
-
-
-
-
- )}
-
-
-
-
-
-
- )}
-
-
- ) : (
-
{
- setPageConsulta(false);
- fetchAppointments();
- }}
- />
- )}
-
- {showDeleteModal && }
+ const dateGrid = useMemo(() => generateDateGrid(), [currentDate]);
+ const weekDays = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
+ const handleDateClick = (day) => setSelectedDay(day);
+ const DeleteModal = () => (
+
+
+
+
+
Confirmação de Cancelamento
+
+
+
Qual o motivo do cancelamento?
+
+
+
+
+
+
+
+
+ );
+
+ useEffect(() => {
+ setQuickJump({
+ month: currentDate.month(),
+ year: currentDate.year(),
+ });
+ }, [currentDate]);
+
+ const filaEsperaFiltrada = useMemo(() => {
+ if (!waitlistSearch.trim()) return filaEsperaData;
+ const term = waitlistSearch.toLowerCase();
+ return filaEsperaData.filter(
+ (item) =>
+ (item?.Infos?.paciente_nome?.toLowerCase() || "").includes(term) ||
+ (item?.Infos?.paciente_cpf?.toLowerCase() || "").includes(term) ||
+ (item?.Infos?.medico_nome?.toLowerCase() || "").includes(term)
);
+ }, [waitlistSearch, filaEsperaData]);
+
+ const applySortingWaitlist = useCallback(
+ (arr) => {
+ if (!Array.isArray(arr) || !waitSortKey) return arr;
+ const copy = [...arr];
+ const key = waitSortKey;
+ const dir = waitSortDir === "asc" ? 1 : -1;
+ copy.sort((a, b) => {
+ const valA =
+ key === "data"
+ ? new Date(a.agendamento.scheduled_at)
+ : a.Infos?.[`${key}_nome`] || "";
+ const valB =
+ key === "data"
+ ? new Date(b.agendamento.scheduled_at)
+ : b.Infos?.[`${key}_nome`] || "";
+ if (valA < valB) return -1 * dir;
+ if (valA > valB) return 1 * dir;
+ return 0;
+ });
+ return copy;
+ },
+ [waitSortKey, waitSortDir]
+ );
+
+ const filaEsperaOrdenada = useMemo(
+ () => applySortingWaitlist(filaEsperaFiltrada),
+ [filaEsperaFiltrada, applySortingWaitlist]
+ );
+ const waitTotalPages =
+ Math.ceil(filaEsperaOrdenada.length / waitPerPage) || 1;
+ const waitIndiceInicial = (waitPage - 1) * waitPerPage;
+ const waitIndiceFinal = waitIndiceInicial + waitPerPage;
+ const filaEsperaPaginada = filaEsperaOrdenada.slice(
+ waitIndiceInicial,
+ waitIndiceFinal
+ );
+
+ const gerarNumerosWaitPages = () => {
+ const paginas = [];
+ const paginasParaMostrar = 5;
+ let inicio = Math.max(1, waitPage - Math.floor(paginasParaMostrar / 2));
+ let fim = Math.min(waitTotalPages, inicio + paginasParaMostrar - 1);
+ inicio = Math.max(1, fim - paginasParaMostrar + 1);
+ for (let i = inicio; i <= fim; i++) paginas.push(i);
+ return paginas;
+ };
+
+ useEffect(() => {
+ setWaitPage(1);
+ }, [waitlistSearch, waitSortKey, waitSortDir]);
+
+ const handleQuickJumpChange = (type, value) => {
+ setQuickJump((prev) => ({ ...prev, [type]: Number(value) }));
+ };
+
+ const applyQuickJump = () => {
+ let newDate = dayjs().year(quickJump.year).month(quickJump.month).date(1);
+ setCurrentDate(newDate);
+ setSelectedDay(newDate);
+ };
+
+ return (
+
+
Agendar nova consulta
+ {!PageNovaConsulta ? (
+
+ {user?.role !== "doctor" && (
+
+
+
+ Filtrar por Médico
+
+
+
handleSearchMedicos(e.target.value)}
+ />
+
+ Buscar médico para filtrar consultas
+
+
+ {searchTermDoctor && FiltredTodosMedicos.length > 0 && (
+
+ {FiltredTodosMedicos.map((medico) => (
+
+ ))}
+
+ )}
+
+
+ {MedicoFiltrado.id !== "vazio" && (
+
+
+
+ {searchTermDoctor}
+
+
+
+ )}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {!FiladeEspera ? (
+
+
+
+ {selectedDay.format("MMM")}
+ {selectedDay.format("DD")}
+
+
+
{selectedDay.format("dddd")}
+
{selectedDay.format("D [de] MMMM [de] YYYY")}
+
+
+
Consultas para {selectedDay.format("DD/MM")}
+ {showSpinner ? (
+
+ ) : DictAgendamentosOrganizados[
+ selectedDay.format("YYYY-MM-DD")
+ ]?.filter(
+ (app) =>
+ MedicoFiltrado.id === "vazio" ||
+ app.doctor_id === MedicoFiltrado.id
+ ).length > 0 ? (
+ DictAgendamentosOrganizados[
+ selectedDay.format("YYYY-MM-DD")
+ ]
+ .filter(
+ (app) =>
+ MedicoFiltrado.id === "vazio" ||
+ app.doctor_id === MedicoFiltrado.id
+ )
+ .map((app) => (
+
+
+ {dayjs(app.scheduled_at).format("HH:mm")}
+
+
+ {app.paciente_nome}
+ Dr(a). {app.medico_nome}
+
+
+ {app.status === "cancelled" ? (
+
+ ) : (
+
+ )}
+ {app.status !== "cancelled" && (
+
+ )}
+
+
+ ))
+ ) : (
+
+
Nenhuma consulta agendada.
+
+ )}
+
+
+
+
+
+ Realizado
+
+
+ Confirmado
+
+
+ Agendado
+
+
+ Cancelado
+
+
+
+
+
{currentDate.format("MMMM [de] YYYY")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {weekDays.map((day) => (
+
+ {day}
+
+ ))}
+ {dateGrid.map((day, index) => {
+ const dayString = day.format("YYYY-MM-DD");
+ const appointmentsOnDay =
+ DictAgendamentosOrganizados[dayString] || [];
+ const filteredAppointments = appointmentsOnDay.filter(
+ (app) =>
+ MedicoFiltrado.id === "vazio" ||
+ app.doctor_id === MedicoFiltrado.id
+ );
+ const cellClasses = `day-cell ${
+ day.isSame(currentDate, "month")
+ ? "current-month"
+ : "other-month"
+ } ${day.isSame(dayjs(), "day") ? "today" : ""} ${
+ day.isSame(selectedDay, "day") ? "selected" : ""
+ }`;
+ return (
+
handleDateClick(day)}
+ >
+
{day.format("D")}
+ {filteredAppointments.length > 0 && (
+
+ {filteredAppointments.length}
+
+ )}
+
+ );
+ })}
+
+
+
+ ) : (
+
+
+
+
+
+
Fila de Espera
+
+
+
+
+ {" "}
+ Filtros
+
+
+
+ setWaitlistSearch(e.target.value)
+ }
+ />
+
+ Digite o nome do paciente, CPF ou nome do médico
+
+
+
+
+
+ Ordenar por:
+
+
+
+
+
+
+ {filaEsperaFiltrada.length} DE{" "}
+ {filaEsperaData.length} SOLICITAÇÕES ENCONTRADAS
+
+
+
+
+
+
+
+ | Nome do Paciente |
+ CPF |
+ Médico Solicitado |
+ Data da Solicitação |
+ Ações |
+
+
+
+ {filaEsperaPaginada.length > 0 ? (
+ filaEsperaPaginada.map((item, index) => (
+
+ | {item?.Infos?.paciente_nome} |
+ {item?.Infos?.paciente_cpf} |
+ {item?.Infos?.medico_nome} |
+
+ {dayjs(
+ item.agendamento.scheduled_at
+ ).format("DD/MM/YYYY")}
+ |
+
+
+ |
+
+ ))
+ ) : (
+
+
+
+ {showSpinner ? (
+
+ ) : (
+ <>
+
+
+ Nenhuma solicitação encontrada.
+
+ >
+ )}
+
+ |
+
+ )}
+
+
+ {filaEsperaFiltrada.length > 0 && (
+
+
+
+ Itens por página:
+
+
+
+
+
+ Página {waitPage} de {waitTotalPages} •
+ Mostrando {waitIndiceInicial + 1}-
+ {Math.min(
+ waitIndiceFinal,
+ filaEsperaFiltrada.length
+ )}{" "}
+ de {filaEsperaFiltrada.length}
+
+
+
+
+ )}
+
+
+
+
+
+
+ )}
+
+
+ ) : (
+
{
+ setPageConsulta(false);
+ fetchAppointments();
+ }}
+ />
+ )}
+ {showDeleteModal && }
+
+ );
};
export default Agendamento;
diff --git a/src/PagesMedico/DoctorRelatorioManager.jsx b/src/PagesMedico/DoctorRelatorioManager.jsx
index 13add57..6f86f00 100644
--- a/src/PagesMedico/DoctorRelatorioManager.jsx
+++ b/src/PagesMedico/DoctorRelatorioManager.jsx
@@ -46,7 +46,7 @@ const DoctorRelatorioManager = () => {
if (authHeader) myHeaders.append('Authorization', authHeader);
const requestOptions = { method: 'GET', headers: myHeaders, redirect: 'follow' };
- // Tenta descobrir o ID do usuário logado para aplicar filtro por médico
+
let userId = null;
let userFullName = null;
try {
@@ -60,12 +60,12 @@ const DoctorRelatorioManager = () => {
console.warn('Não foi possível obter UserInfos (pode não estar logado):', err);
}
- // Monta a URL com possíveis filtros preferenciais
+
const baseUrl = "https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/reports?select=*";
let data = [];
if (userId) {
- // 1) tenta por doctor_id
+
try {
const res = await fetch(`${baseUrl}&doctor_id=eq.${userId}`, requestOptions);
data = await res.json();
@@ -74,7 +74,7 @@ const DoctorRelatorioManager = () => {
data = [];
}
- // 2) fallback para created_by (se vazio)
+
if ((!Array.isArray(data) || data.length === 0) && userId) {
try {
const res2 = await fetch(`${baseUrl}&created_by=eq.${userId}`, requestOptions);
@@ -85,10 +85,9 @@ const DoctorRelatorioManager = () => {
}
}
- // 3) fallback para requested_by com nome completo (se ainda vazio)
if ((!Array.isArray(data) || data.length === 0) && userFullName) {
try {
- // encode para evitar problemas com espaços/caracteres especiais
+
const encodedName = encodeURIComponent(userFullName);
const res3 = await fetch(`${baseUrl}&requested_by=eq.${encodedName}`, requestOptions);
data = await res3.json();
@@ -99,7 +98,6 @@ const DoctorRelatorioManager = () => {
}
}
- // Se não obteve userId ou nenhuma das tentativas acima retornou algo, busca tudo (comportamento anterior)
if (!userId || (!Array.isArray(data) || data.length === 0)) {
try {
const resAll = await fetch(baseUrl, requestOptions);
@@ -110,7 +108,7 @@ const DoctorRelatorioManager = () => {
}
}
- // garante unicidade e ordenação por criação
+
const uniqueMap = new Map();
(Array.isArray(data) ? data : []).forEach(r => {
if (r && r.id) uniqueMap.set(r.id, r);
@@ -144,14 +142,14 @@ const DoctorRelatorioManager = () => {
};
}, [authHeader]);
- // Busca dados de pacientes e médicos baseados na lista final que aparece na tela
+
useEffect(() => {
const fetchRelData = async () => {
const pacientes = [];
const medicos = [];
for (let i = 0; i < relatoriosFinais.length; i++) {
const rel = relatoriosFinais[i];
- // paciente
+
try {
const pacienteRes = await GetPatientByID(rel.patient_id, authHeader);
pacientes.push(Array.isArray(pacienteRes) ? pacienteRes[0] : pacienteRes);
@@ -159,13 +157,12 @@ const DoctorRelatorioManager = () => {
pacientes.push(null);
}
- // médico: prioriza campos com id (doctor_id ou created_by). Se tiver somente requested_by (nome), usa nome.
try {
if (rel.doctor_id) {
const docRes = await GetDoctorByID(rel.doctor_id, authHeader);
medicos.push(Array.isArray(docRes) ? docRes[0] : docRes);
} else if (rel.created_by) {
- // created_by costuma ser id
+
const docRes = await GetDoctorByID(rel.created_by, authHeader);
medicos.push(Array.isArray(docRes) ? docRes[0] : docRes);
} else if (rel.requested_by) {
@@ -174,7 +171,6 @@ const DoctorRelatorioManager = () => {
medicos.push({ full_name: '' });
}
} catch (err) {
- // fallback para requested_by se houver
medicos.push({ full_name: rel.requested_by || '' });
}
}
@@ -190,7 +186,6 @@ const DoctorRelatorioManager = () => {
}, [relatoriosFinais, authHeader]);
const abrirModal = (relatorio, pageIndex) => {
- // encontra índice global do relatório no array relatoriosFinais (para alinhar com pacientes/medicos)
const globalIndex = relatoriosFinais.findIndex(r => r.id === relatorio.id);
const indexToUse = globalIndex >= 0 ? globalIndex : (indiceInicial + pageIndex);
setRelatorioModal(relatorio);
@@ -198,7 +193,7 @@ const DoctorRelatorioManager = () => {
setShowModal(true);
};
- // Função para limpar filtros
+
const limparFiltros = () => {
setTermoPesquisa('');
setFiltroExame('');
@@ -256,12 +251,11 @@ const DoctorRelatorioManager = () => {
return (
{showModal && (
-
setShowModal(false)}>
-
e.stopPropagation()}>
+
+
-
+
Relatório de {pacientesComRelatorios[modalIndex]?.full_name}
-
@@ -290,11 +284,11 @@ const DoctorRelatorioManager = () => {
-
+
-
diff --git a/src/PagesMedico/NovoRelatorioAudio.jsx b/src/PagesMedico/NovoRelatorioAudio.jsx
new file mode 100644
index 0000000..6012435
--- /dev/null
+++ b/src/PagesMedico/NovoRelatorioAudio.jsx
@@ -0,0 +1,215 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import html2pdf from 'html2pdf.js';
+import './styleMedico/NovoRelatorioAudio.css';
+
+const NovoRelatorioAudio = () => {
+ const navigate = useNavigate();
+ const [loading, setLoading] = useState(false);
+
+ const [formData, setFormData] = useState({
+ paciente_nome: '',
+ data_exame: new Date().toISOString().split('T')[0],
+ exam: '',
+ diagnostico: '',
+ conclusao: '',
+ medico_nome: 'Dr.______________________'
+ });
+
+ const handleAudioUpload = async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ setLoading(true);
+ const data = new FormData();
+ data.append('audio', file);
+
+ try {
+ const response = await fetch('http://localhost:3001/api/transcrever-relatorio', {
+ method: 'POST',
+ body: data,
+ });
+
+ if (!response.ok) throw new Error('Erro na comunicação com a API');
+
+ const result = await response.json();
+
+ setFormData(prev => ({
+ ...prev,
+ exam: result.exam || prev.exam,
+ diagnostico: result.diagnostico || prev.diagnostico,
+ conclusao: result.conclusao || prev.conclusao
+ }));
+
+ } catch (error) {
+ console.error(error);
+ alert("Erro: Verifique se o backend (porta 3001) está rodando.");
+ } finally {
+ setLoading(false);
+ e.target.value = null;
+ }
+ };
+
+ const handleChange = (e) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ const handlePrintPDF = () => {
+ const element = document.getElementById('documento-final');
+ const opt = {
+ margin: 0,
+ filename: `Laudo_${formData.paciente_nome || 'SemNome'}.pdf`,
+ image: { type: 'jpeg', quality: 0.98 },
+ html2canvas: { scale: 2 },
+ jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
+ };
+ html2pdf().set(opt).from(element).save();
+ };
+
+ return (
+
+
+
+
+
AI Report
+
Gerador de laudos automatizado
+
+
+
+ {loading ? (
+
+ ) : (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ navigate(-1)}>Voltar
+
+ PDF
+
+
+
+
+
+
+
+
+
+
CLÍNICA RISE UP
+
Excelência em Diagnóstico por Imagem
+
+
+
{formData.medico_nome}
+
CRM/SP 123456
+
{new Date().toLocaleDateString()}
+
+
+
+
+
+
+
+ | Paciente: |
+ {formData.paciente_nome || '__________________________'} |
+
+
+ | Exame: |
+ {formData.exam || '__________________________'} |
+
+
+ | Data: |
+ {new Date(formData.data_exame).toLocaleDateString('pt-BR')} |
+
+
+
+
+
+
+
Relatório Médico
+
+
Análise:
+
+ {formData.diagnostico || O diagnóstico aparecerá aqui após o processamento do áudio...}
+
+
+
Conclusão:
+
+ {formData.conclusao}
+
+
+
+
+
________________________________________
+
{formData.medico_nome}
+
Assinatura Eletrônica
+
+
+
+
+
+ );
+};
+
+export default NovoRelatorioAudio;
\ No newline at end of file
diff --git a/src/PagesMedico/styleMedico/Agendamento.css b/src/PagesMedico/styleMedico/Agendamento.css
index ffa5552..5f01d68 100644
--- a/src/PagesMedico/styleMedico/Agendamento.css
+++ b/src/PagesMedico/styleMedico/Agendamento.css
@@ -6,18 +6,58 @@
/* --- Posiciona a barra de busca corretamente --- */
.busca-atendimento {
display: flex;
- align-items: center; /* Alinha os itens verticalmente */
- margin-top: 20px; /* Espaço acima da barra de busca */
- padding: 0 10px; /* Adiciona um padding lateral para alinhar com o resto */
+ align-items: center;
+ margin-top: 20px;
+ padding: 0 10px;
gap: 15px;
}
.busca-atendimento > div:first-child {
- width: 400px; /* Define um tamanho para a barra de pesquisa */
+ width: 400px;
display: flex;
align-items: center;
}
+@media (max-width: 768px) {
+ .busca-atendimento {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .busca-atendimento > div:first-child {
+ width: 100%;
+ }
+
+ .btns-e-legenda-container {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 10px;
+ }
+
+ .legenda-tabela {
+ flex-wrap: wrap;
+ gap: 8px;
+ }
+}
+
+@media (max-width: 576px) {
+ .busca-atendimento {
+ padding: 0 5px;
+ }
+
+ .btns-e-legenda-container {
+ padding: 0 5px;
+ }
+
+ .btn-selecionar-tabeladia,
+ .btn-selecionar-tabelasemana,
+ .btn-selecionar-tabelames {
+ padding: 6px 8px;
+ font-size: medium;
+ }
+}
+
.busca-atendimento input {
margin-left: 8px;
border-radius: 8px;
@@ -140,6 +180,7 @@
border-bottom: 3px solid transparent;
padding: 8px;
border-radius: 10px 10px 0px 0px;
+ color: #fff;
font-weight: bold;
cursor: pointer;
}
diff --git a/src/PagesMedico/styleMedico/NovoRelatorioAudio.css b/src/PagesMedico/styleMedico/NovoRelatorioAudio.css
new file mode 100644
index 0000000..591eef3
--- /dev/null
+++ b/src/PagesMedico/styleMedico/NovoRelatorioAudio.css
@@ -0,0 +1,119 @@
+/* Container que ocupa toda a altura da tela (menos o header do site) */
+.ai-editor-container {
+ display: flex;
+ min-height: calc(100vh - 80px); /* Ajuste conforme seu header */
+ background-color: #e9ecef; /* Cor de "Mesa" */
+ overflow: hidden;
+}
+
+/* --- LADO ESQUERDO: PAINEL DE CONTROLE --- */
+.editor-sidebar {
+ width: 400px;
+ background-color: #212529; /* Dark Mode para o painel */
+ color: #fff;
+ padding: 20px;
+ overflow-y: auto;
+ display: flex;
+ flex-direction: column;
+ gap: 15px;
+ box-shadow: 4px 0 10px rgba(0,0,0,0.1);
+}
+
+.editor-sidebar h4 {
+ color: #0dcaf0; /* Ciano para destaque */
+ margin-bottom: 20px;
+ font-weight: 700;
+}
+
+.editor-sidebar label {
+ font-size: 0.85rem;
+ color: #adb5bd;
+ margin-bottom: 5px;
+}
+
+/* Estilo customizado para inputs no fundo escuro */
+.dark-input {
+ background-color: #343a40;
+ border: 1px solid #495057;
+ color: #fff;
+ border-radius: 6px;
+}
+.dark-input:focus {
+ background-color: #3b4248;
+ color: #fff;
+ border-color: #0dcaf0;
+ box-shadow: none;
+}
+
+/* Área de Upload Destacada */
+.ai-upload-box {
+ border: 2px dashed #0dcaf0;
+ border-radius: 10px;
+ padding: 20px;
+ text-align: center;
+ background: rgba(13, 202, 240, 0.1);
+ transition: 0.3s;
+ cursor: pointer;
+}
+.ai-upload-box:hover {
+ background: rgba(13, 202, 240, 0.2);
+}
+
+/* --- LADO DIREITO: PREVIEW A4 --- */
+.preview-area {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ padding: 40px;
+ overflow-y: auto;
+}
+
+.paper-a4 {
+ width: 210mm;
+ min-height: 297mm;
+ background: white;
+ padding: 25mm;
+ box-shadow: 0 0 20px rgba(0,0,0,0.15);
+ color: #000;
+ font-family: 'Georgia', serif; /* Fonte mais séria para o documento */
+ font-size: 12pt;
+ line-height: 1.6;
+}
+
+/* Responsividade: Em celular vira coluna única */
+@media (max-width: 900px) {
+ .ai-editor-container {
+ flex-direction: column;
+ min-height: auto;
+ }
+ .editor-sidebar {
+ width: 100%;
+ height: auto;
+ box-shadow: 0 4px 10px rgba(0,0,0,0.1);
+ }
+ .preview-area {
+ padding: 20px;
+ }
+ .paper-a4 {
+ width: 100%;
+ min-height: auto;
+ padding: 15mm;
+ box-shadow: none;
+ }
+}
+
+@media (max-width: 576px) {
+ .ai-editor-container {
+ padding: 0;
+ }
+ .editor-sidebar {
+ padding: 15px;
+ }
+ .preview-area {
+ padding: 10px;
+ }
+ .paper-a4 {
+ padding: 10mm;
+ }
+}
\ No newline at end of file
diff --git a/src/PagesPaciente/style.css b/src/PagesPaciente/style.css
index 2d0b21f..e675e55 100644
--- a/src/PagesPaciente/style.css
+++ b/src/PagesPaciente/style.css
@@ -1,14 +1,71 @@
/* Estilo geral do card para agrupar e dar um formato */
.card-consulta {
- background-color: #007bff; /* Um tom de azul padrão */
- display: flex; /* Para colocar horário e info lado a lado */
- border-radius: 10px; /* Cantos arredondados */
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); /* Sombra suave */
- overflow: hidden; /* Garante que o fundo azul não 'vaze' */
- /* width: 280px; /* Largura de exemplo */
+ background-color: #007bff;
+ display: flex;
+ border-radius: 10px;
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
margin: 20px;
- font-family: Arial, sans-serif; /* Fonte legível */
+ font-family: Arial, sans-serif;
+}
+
+@media (max-width: 768px) {
+ .card-consulta {
+ flex-direction: column;
+ margin: 10px;
+ }
+
+ .horario-container {
+ border-right: none;
+ border-bottom: 1px solid #0056b3;
+ padding: 10px 15px;
+ }
+
+ .horario {
+ font-size: 1.8em;
+ }
+
+ .info-container {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 1rem;
+ padding: 15px;
+ }
+
+ .actions-container {
+ opacity: 1;
+ visibility: visible;
+ background: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ border: none;
+ box-shadow: none;
+ margin-left: 0;
+ padding: 0;
+ justify-content: flex-start;
+ margin-top: 10px;
+ }
+
+ .card-consulta:hover .actions-container {
+ transform: none;
+ }
+}
+
+@media (max-width: 576px) {
+ .horario {
+ font-size: 1.5em;
+ }
+
+ .informacao {
+ font-size: 1em;
+ }
+
+ .btn-edit-custom-style,
+ .btn-delete-custom-style {
+ padding: 6px 10px;
+ font-size: 0.8rem;
+ }
}
/* 1. Estilo para o Horário (Fundo Azul e Texto Branco/Grande) */
diff --git a/src/components/AgendarConsulta/style/card-consulta.css b/src/components/AgendarConsulta/style/card-consulta.css
index 34d8dea..285a558 100644
--- a/src/components/AgendarConsulta/style/card-consulta.css
+++ b/src/components/AgendarConsulta/style/card-consulta.css
@@ -1,3 +1,10 @@
+@media (max-width: 768px) {
+ .container-cardconsulta {
+ padding-right: 80px; /* Espaço para os botões */
+ position: relative;
+ }
+}
+
.actions-container {
display: flex;
gap: 8px;
@@ -84,4 +91,50 @@
.tabelamensal .container-cardconsulta{
width: 24rem;
+}
+
+@media (max-width: 768px) {
+ .actions-container {
+ opacity: 1;
+ visibility: visible;
+ background: none;
+ backdrop-filter: none;
+ -webkit-backdrop-filter: none;
+ border: none;
+ box-shadow: none;
+ margin-left: 0;
+ padding: 0;
+ justify-content: flex-end;
+ position: absolute;
+ top: 10px;
+ right: 10px;
+ }
+
+ .container-cardconsulta:hover .actions-container {
+ transform: none;
+ }
+
+ .tabelamensal .container-cardconsulta {
+ width: 100%;
+ max-width: 100%;
+ }
+}
+
+@media (max-width: 576px) {
+ .container-cardconsulta {
+ padding-right: 10px; /* Remove o padding extra para telas muito pequenas */
+ }
+
+ .actions-container {
+ position: static;
+ margin-top: 10px;
+ justify-content: flex-start;
+ }
+
+ .btn-edit-custom-style,
+ .btn-delete-custom-style,
+ .btn-confirm-style {
+ padding: 6px 10px;
+ font-size: 0.8rem;
+ }
}
\ No newline at end of file
diff --git a/src/components/AgendarConsulta/style/formagendamentos.css b/src/components/AgendarConsulta/style/formagendamentos.css
index 3fcf61f..f83515c 100644
--- a/src/components/AgendarConsulta/style/formagendamentos.css
+++ b/src/components/AgendarConsulta/style/formagendamentos.css
@@ -183,6 +183,67 @@ textarea{
gap: 16px;
}
+@media (max-width: 768px) {
+ .campos-informacoes-paciente,
+ .campo-informacoes-atendimento {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ #informacoes-atendimento-segunda-linha {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ #informacoes-atendimento-segunda-linha-esquerda select[name="unidade"],
+ input[type="time"],
+ select[name=solicitante],
+ .campo-de-input {
+ width: 100% !important;
+ max-width: 100%;
+ }
+
+ .tipo_atendimento {
+ margin-left: 0;
+ }
+
+ .linha {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 10px;
+ }
+
+ .sessao-contador {
+ width: 100px;
+ }
+}
+
+@media (max-width: 576px) {
+ .form-container {
+ padding: 15px;
+ }
+
+ .form-title {
+ font-size: 22px;
+ }
+
+ .form-agendamento input,
+ .form-agendamento select,
+ .form-agendamento textarea {
+ font-size: 13px;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ gap: 10px;
+ }
+
+ .btn-primary,
+ .btn-cancel {
+ width: 100%;
+ }
+}
+
.campo-de-input {
flex: 1;
display: flex;
diff --git a/src/components/FormRelatorio.jsx b/src/components/FormRelatorio.jsx
index 7c04a49..fb7a01e 100644
--- a/src/components/FormRelatorio.jsx
+++ b/src/components/FormRelatorio.jsx
@@ -1,55 +1,97 @@
-import React from 'react'
+import React, { useState } from 'react'
import '../PagesMedico/styleMedico/FormNovoRelatorio.css'
-import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuth } from '../components/utils/AuthProvider'
import { GetPatientByCPF } from '../components/utils/Functions-Endpoints/Patient'
import { FormatCPF } from '../components/utils/Formatar/Format'
import html2pdf from 'html2pdf.js'
-const FormRelatorio = ({onSave, DictInfo, setDictInfo }) => {
- const {getAuthorizationHeader} = useAuth()
+const FormRelatorio = ({ onSave, DictInfo, setDictInfo }) => {
+ const { getAuthorizationHeader } = useAuth()
let authHeader = getAuthorizationHeader()
- const navigate= useNavigate()
-
+ const navigate = useNavigate()
+
const [showModal, setShowModal] = useState(false)
+
+ // --- NOVO: Estado para controlar o loading da transcrição ---
+ const [isTranscribing, setIsTranscribing] = useState(false);
+
+ // --- NOVA FUNÇÃO: Envia o áudio e preenche o formulário ---
+ const handleAudioUpload = async (e) => {
+ const file = e.target.files[0];
+ if (!file) return;
+
+ setIsTranscribing(true); // Ativa o spinner
+
+ const formData = new FormData();
+ formData.append('audio', file); // 'audio' deve ser o nome esperado no backend
+
+ try {
+ // ⚠️ ATENÇÃO: Substitua essa URL pela rota do seu backend que criamos
+ const response = await fetch('http://localhost:3001/api/transcrever-relatorio', {
+ method: 'POST',
+ body: formData,
+ // headers: { 'Authorization': authHeader } // Descomente se seu backend precisar de token
+ });
+
+ if (!response.ok) throw new Error("Falha na transcrição");
+
+ const data = await response.json();
+
+ // Atualiza o DictInfo com os dados vindos da IA
+ setDictInfo((prev) => ({
+ ...prev,
+ exam: data.exam || prev.exam, // Preenche se a IA achou, senão mantém o antigo
+ diagnostico: data.diagnostico || prev.diagnostico,
+ conclusao: data.conclusao || prev.conclusao
+ }));
+
+ } catch (error) {
+ console.error("Erro no upload de áudio:", error);
+ alert("Não foi possível gerar o relatório por áudio. Verifique o backend.");
+ } finally {
+ setIsTranscribing(false); // Desativa o spinner
+ e.target.value = null; // Limpa o input para permitir enviar o mesmo arquivo novamente se quiser
+ }
+ };
+ // -----------------------------------------------------------
const BaixarPDFdoRelatorio = () => {
const elemento = document.getElementById("folhaA4"); // tua div do relatório
- const opt = {
- margin: 0,
- filename: `relatorio_${DictInfo?.paciente_nome || "paciente"}.pdf`,
- html2canvas: { scale: 2 },
- jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
- };
+ const opt = {
+ margin: 0,
+ filename: `relatorio_${DictInfo?.paciente_nome || "paciente"}.pdf`,
+ html2canvas: { scale: 2 },
+ jsPDF: { unit: "mm", format: "a4", orientation: "portrait" },
+ };
- html2pdf().set(opt).from(elemento).save();
+ html2pdf().set(opt).from(elemento).save();
}
const handleChange = (e) => {
const { name, value } = e.target;
console.log(name, value)
- if(name === 'paciente_cpf') {
- const formattedCPF = FormatCPF(value);
- setDictInfo((prev) => ({ ...prev, [name]: formattedCPF }));
+ if (name === 'paciente_cpf') {
+ const formattedCPF = FormatCPF(value);
+ setDictInfo((prev) => ({ ...prev, [name]: formattedCPF }));
- const fetchPatient = async () => {
- const patientData = await GetPatientByCPF(formattedCPF, authHeader);
- if (patientData) {
- setDictInfo((prev) => ({
- ...prev,
- paciente_cpf:value,
- paciente_nome: patientData.full_name,
- paciente_id: patientData.id
- }));
+ const fetchPatient = async () => {
+ const patientData = await GetPatientByCPF(formattedCPF, authHeader);
+ if (patientData) {
+ setDictInfo((prev) => ({
+ ...prev,
+ paciente_cpf: value,
+ paciente_nome: patientData.full_name,
+ paciente_id: patientData.id
+ }));
+ }
+
+ };
+ if (formattedCPF.length === 14) {
+ fetchPatient();
}
-
- };
- if(formattedCPF.length === 14){
- fetchPatient();
- }
- }else{
- setDictInfo((prev) => ({ ...prev, [name]: value }));
+ } else {
+ setDictInfo((prev) => ({ ...prev, [name]: value }));
}
}
@@ -57,145 +99,164 @@ const FormRelatorio = ({onSave, DictInfo, setDictInfo }) => {
e.preventDefault();
console.log(DictInfo)
setShowModal(true)
-
-
-onSave({
- "patient_id": DictInfo.paciente_id,
-
- "exam": DictInfo.exam,
- "diagnosis": DictInfo.diagnosis,
- "conclusion": DictInfo.conclusao,
- "status": "draft",
- "requested_by": DictInfo.requested_by,
-
- "hide_date": false,
- "hide_signature": false,
-});
+ onSave({
+ "patient_id": DictInfo.paciente_id,
+ "exam": DictInfo.exam,
+ "diagnosis": DictInfo.diagnostico, // Garanta que o backend espera 'diagnosis' mas seu state usa 'diagnostico'
+ "conclusion": DictInfo.conclusao,
+ "status": "draft",
+ "requested_by": DictInfo.requested_by,
+ "hide_date": false,
+ "hide_signature": false,
+ });
}
- return (
-
- {showModal &&(
-
-
-
-
-
Relatório criado com sucesso
-
setShowModal(false)}
- >
+ return (
+
+ {showModal && (
+
+
+
+
+
Relatório criado com sucesso
+ setShowModal(false)}
+ >
+
+
+
Você também pode baixa-lo agora em pdf
+
+
+ baixar em pdf
+
+ { setShowModal(false); navigate(('/medico/relatorios')) }}
+ >
+ Fechar
+
+
+
+
-
-
Você também pode baixa-lo agora em pdf
-
-
- baixar em pdf
+ )}
- {setShowModal(false); navigate(('/medico/relatorios'))}}
- >
- Fechar
-
-
-
-
-
- )}
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Salvar
+
+
+
+
+
+
Modelo do relatório
+
+
+
+
+
+
Paciente: {DictInfo?.paciente_nome}
+
Data de nascimento:
+
+ {/* Corrigi de data_exam para data_exame para bater com o state */}
+
Data do exame: {DictInfo.data_exame}
+
+
Exame: {DictInfo.exam}
+
+
Diagnostico: {DictInfo.diagnostico}
+
+
Conclusão: {DictInfo.conclusao}
+
+
+
+
+
Dr {DictInfo.requested_by}
+
Emitido em: {new Date().toLocaleDateString()}
+
+
+
+
+
+ )
}
export default FormRelatorio
\ No newline at end of file
diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css
index 0a4737d..292356f 100644
--- a/src/components/Header/Header.css
+++ b/src/components/Header/Header.css
@@ -223,6 +223,8 @@
z-index: 2001;
margin-top: 80px;
margin-right: 20px;
+ /* Adicionado para responsividade */
+ max-width: 90vw;
}
.suporte-card {
@@ -309,6 +311,8 @@
z-index: 3001;
margin-top: 80px;
margin-right: 20px;
+ /* Adicionado para responsividade */
+ max-width: 90vw;
}
.chat-online {
@@ -478,6 +482,7 @@
.suporte-card-container,
.chat-container {
+ margin-top: 60px;
margin-right: 10px;
margin-left: 10px;
}
@@ -489,28 +494,101 @@
}
}
-/* permite que cliques "passem" através do header (exceto para os elementos interativos) */
-.header-container {
- pointer-events: none; /* header não captura cliques */
+@media (max-width: 576px) {
+ .header-container {
+ padding: 8px 10px;
+ }
+
+ .right-corner-elements {
+ gap: 10px;
+ }
+
+ .profile-picture-container {
+ width: 35px;
+ height: 35px;
+ }
+
+ .phone-icon-container {
+ font-size: 20px;
+ }
+
+ .suporte-card-container,
+ .chat-container {
+ margin-top: 50px;
+ margin-right: 5px;
+ margin-left: 5px;
+ }
+
+ .suporte-card {
+ padding: 1rem;
+ }
+
+ .chat-online {
+ width: calc(100vw - 10px);
+ height: 80vh; /* Limita a altura para telas pequenas */
+ }
+
+ .chat-input {
+ padding: 0.75rem;
+ }
+
+ .chat-campo {
+ padding: 0.5rem;
+ }
+
+ .chat-enviar {
+ padding: 0.5rem 1rem;
+ }
}
-/* mas permite que os controles no canto (telefone e profile) continuem clicáveis */
+@media (max-width: 768px) {
+ .header-container {
+ padding: 10px 15px;
+ }
+
+ .right-corner-elements {
+ gap: 15px;
+ }
+
+ .profile-picture-container {
+ width: 40px;
+ height: 40px;
+ }
+
+ .suporte-card-container,
+ .chat-container {
+ margin-right: 10px;
+ margin-left: 10px;
+ }
+
+ .suporte-card,
+ .chat-online {
+ width: calc(100vw - 20px);
+ max-width: none;
+ }
+}
+
+.header-container {
+ pointer-events: none;
+}
+
+
.phone-icon-container,
.profile-section {
pointer-events: auto;
}
-/* Garantir pointer-events nos elementos do header e overlays criados por portal */
+
.header-container { pointer-events: auto; }
.phone-icon-container, .profile-section { pointer-events: auto; }
-/* Força que os overlays criados por portal fiquem por cima */
+
.logout-modal-overlay, .suporte-card-overlay, .chat-overlay {
z-index: 110000 !important;
pointer-events: auto !important;
}
-/* Pequeno ajuste visual dos botões do modal (pode se misturar com seu CSS atual) */
+
.logout-cancel-button {
padding: 10px 18px;
border-radius: 8px;
diff --git a/src/components/utils/supabaseClient.js b/src/components/utils/supabaseClient.js
deleted file mode 100644
index 1590360..0000000
--- a/src/components/utils/supabaseClient.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// src/utils/supabaseClient.js
-import { createClient } from "@supabase/supabase-js";
-import API_KEY from "./apiKeys";
-
-const SUPABASE_URL = "https://yuanqfswhberkoevtmfr.supabase.co";
-
-export const supabase = createClient(SUPABASE_URL, API_KEY);
diff --git a/src/data/sidebar-items-adm.json b/src/data/sidebar-items-adm.json
index 5b4c554..1eb3da2 100644
--- a/src/data/sidebar-items-adm.json
+++ b/src/data/sidebar-items-adm.json
@@ -25,5 +25,10 @@
"name": "Painel Administrativo",
"icon": "file-bar-graph-fill",
"url": "/admin/painel"
+ },
+ {
+ "name": "Gestão de Usuários",
+ "icon": "people-fill",
+ "url": "/admin/gestao"
}
]
diff --git a/src/data/sidebar-items-medico.json b/src/data/sidebar-items-medico.json
index 0ededdb..b023323 100644
--- a/src/data/sidebar-items-medico.json
+++ b/src/data/sidebar-items-medico.json
@@ -5,17 +5,26 @@
"url": "/medico/agendamento"
},
+
+
+ {
+ "name": "Relatório por Áudio",
+ "icon": "file-earmark-plus-fill",
+ "url": "/medico/novo-relatorio-audio"
+ },
+
{
"name": "Relatórios",
"icon": "file-earmark-text-fill",
"url": "/medico/relatorios"
},
+
{
"name": "Chat com pacientes",
"icon": "chat-dots-fill",
"url": "/medico/chat"
}
-
+
]
diff --git a/src/index.css b/src/index.css
index bd5bd6d..43cf021 100644
--- a/src/index.css
+++ b/src/index.css
@@ -1,13 +1,19 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+ sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+code {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
+ monospace;
+}
diff --git a/src/pages/DisponibilidadesDoctorPage.jsx b/src/pages/DisponibilidadesDoctorPage.jsx
index ef488d8..5f79a67 100644
--- a/src/pages/DisponibilidadesDoctorPage.jsx
+++ b/src/pages/DisponibilidadesDoctorPage.jsx
@@ -38,6 +38,9 @@ const DisponibilidadesDoctorPage = () => {
const [expandedDoctors, setExpandedDoctors] = useState({});
const [showSuggestions, setShowSuggestions] = useState(false);
const [availabilityEdit, setAvailabilityEdit] = useState([]);
+ // Add the missing state variables
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+ const [selectedDisponibilidadeId, setSelectedDisponibilidadeId] = useState(null);
const getHeaders = () => {
const myHeaders = new Headers();
@@ -170,8 +173,24 @@ const DisponibilidadesDoctorPage = () => {
if (!window.confirm("Deseja realmente excluir esta disponibilidade?")) return;
try {
const res = await fetch(`${ENDPOINT}?id=eq.${id}`, { method: "DELETE", headers: getHeaders() });
- if (res.ok) setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
- } catch (error) {}
+ if (res.ok) {
+ setDisponibilidades((prev) => prev.filter((d) => d.id !== id));
+ setShowDeleteModal(false);
+ setSelectedDisponibilidadeId(null);
+ }
+ } catch (error) {
+ console.error("Erro ao excluir disponibilidade:", error);
+ }
+ };
+
+ const handleOpenDeleteModal = (id) => {
+ setSelectedDisponibilidadeId(id);
+ setShowDeleteModal(true);
+ };
+
+ const handleCloseDeleteModal = () => {
+ setShowDeleteModal(false);
+ setSelectedDisponibilidadeId(null);
};
const disponibilidadesAgrupadas = useMemo(() => {
@@ -420,7 +439,16 @@ const DisponibilidadesDoctorPage = () => {
{getStatusText(disp)}
|
-
{!disp.is_empty && deletarDisponibilidade(disp.id)} className="disp-btn-delete">Excluir} |
+
+ {!disp.is_empty && (
+ handleOpenDeleteModal(disp.id)}
+ className="disp-btn-delete"
+ >
+ Excluir
+
+ )}
+ |
))}
@@ -435,6 +463,51 @@ const DisponibilidadesDoctorPage = () => {
)}
+
+ {showDeleteModal && (
+
+
+
+
+
+ Confirmação de Exclusão
+
+
+
+
+
+ Tem certeza que deseja excluir esta disponibilidade?
+
+
+
+
+
+ Cancelar
+
+
+ deletarDisponibilidade(selectedDisponibilidadeId)}
+ >
+ Excluir
+
+
+
+
+
+ )}
);
};
diff --git a/src/pages/DoctorTable.jsx b/src/pages/DoctorTable.jsx
index a0f3650..e5becd7 100644
--- a/src/pages/DoctorTable.jsx
+++ b/src/pages/DoctorTable.jsx
@@ -27,9 +27,8 @@ function TableDoctor({setDictInfo}) {
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [selectedDoctorId, setSelectedDoctorId] = useState(null);
- // Ordenação rápida
- const [sortKey, setSortKey] = useState(null); // 'nome' | 'idade' | null
- const [sortDir, setSortDir] = useState('asc'); // 'asc' | 'desc'
+ const [sortKey, setSortKey] = useState(null);
+ const [sortDir, setSortDir] = useState('asc');
const limparFiltros = () => {
setSearch("");
@@ -147,7 +146,6 @@ function TableDoctor({setDictInfo}) {
return resultado;
}) : [];
-
const applySorting = (arr) => {
if (!Array.isArray(arr) || !sortKey) return arr;
const copy = [...arr];
@@ -277,7 +275,7 @@ function TableDoctor({setDictInfo}) {
- {/* Ordenação rápida */}
+
Ordenar por:
@@ -481,7 +479,6 @@ function TableDoctor({setDictInfo}) {
- {/* Paginação */}
{medicosFiltrados.length > 0 && (
@@ -554,15 +551,10 @@ function TableDoctor({setDictInfo}) {
>
-
+
Confirmação de Exclusão
- setShowDeleteModal(false)}
- >
diff --git a/src/pages/EditPage.jsx b/src/pages/EditPage.jsx
index 0f9bb18..dac83d8 100644
--- a/src/pages/EditPage.jsx
+++ b/src/pages/EditPage.jsx
@@ -9,6 +9,7 @@ import { useAuth } from '../components/utils/AuthProvider'
const EditPage = ({DictInfo}) => {
const navigate = useNavigate()
const [PatientToPUT, setPatientPUT] = useState({})
+ const [showSuccessModal, setShowSuccessModal] = useState(false)
const { getAuthorizationHeader, isAuthenticated } = useAuth();
@@ -37,9 +38,15 @@ const HandlePutPatient = async () => {
};
fetch(`https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/patients?id=eq.${PatientToPUT.id}`,requestOptions)
- .then(response => console.log(response))
+ .then(response => {
+ console.log(response)
+ if (response.ok) {
+ setShowSuccessModal(true)
+ }
+ return response
+ })
.then(result => console.log(result))
- .catch(console.log("erro"))
+ .catch(error => console.log("erro", error))
};
@@ -52,6 +59,46 @@ const HandlePutPatient = async () => {
formData={PatientToPUT}
setFormData={setPatientPUT}
/>
+
+ {showSuccessModal && (
+
+
+
+
+
+ Paciente Editado
+
+
+
+
+
+ Paciente editado com sucesso!
+
+
+
+
+ {
+ setShowSuccessModal(false)
+ navigate(-1)
+ }}
+ >
+ OK
+
+
+
+
+
+ )}
)
diff --git a/src/pages/ExcecoesDisponibilidade.jsx b/src/pages/ExcecoesDisponibilidade.jsx
index db2e034..e4f834b 100644
--- a/src/pages/ExcecoesDisponibilidade.jsx
+++ b/src/pages/ExcecoesDisponibilidade.jsx
@@ -1,4 +1,5 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
+import { useNavigate } from 'react-router-dom';
import dayjs from 'dayjs';
import 'dayjs/locale/pt-br';
import weekday from 'dayjs/plugin/weekday';
@@ -23,12 +24,10 @@ const getDateRange = (date, view) => {
toDate = startDayjs.format('YYYY-MM-DD');
titleRange = startDayjs.format('DD/MM/YYYY');
} else if (view === 'semanal') {
- // Padrão Dayjs: Sunday=0, Monday=1.
- // startOf('week') pode ser Domingo ou Segunda, dependendo do locale.
- // Se precisar forçar a Segunda-feira:
+
let weekStart = startDayjs.startOf('week');
- if (weekStart.day() !== 1) { // Se não for segunda-feira (1), ajusta
- weekStart = startDayjs.weekday(1); // Vai para a segunda-feira desta semana
+ if (weekStart.day() !== 1) {
+ weekStart = startDayjs.weekday(1);
}
const weekEnd = weekStart.add(6, 'day');
@@ -52,12 +51,21 @@ const getDateRange = (date, view) => {
const ExcecoesDisponibilidade = () => {
const { getAuthorizationHeader } = useAuth();
+ const navigate = useNavigate();
const [pageNovaExcecao, setPageNovaExcecao] = useState(false);
const [excecoes, setExcecoes] = useState([]);
const [loading, setLoading] = useState(false);
+ const [showDeleteModal, setShowDeleteModal] = useState(false);
+ const [selectedExceptionId, setSelectedExceptionId] = useState(null);
+ const [showSuccessModal, setShowSuccessModal] = useState(false);
+ const [successMessage, setSuccessMessage] = useState('');
const [filtroMedicoId, setFiltroMedicoId] = useState('');
const [filtroData, setFiltroData] = useState(dayjs().format('YYYY-MM-DD'));
+ const [listaDeMedicos, setListaDeMedicos] = useState([]);
+ const [searchTermDoctor, setSearchTermDoctor] = useState('');
+ const [filteredDoctors, setFilteredDoctors] = useState([]);
+ const [selectedDoctor, setSelectedDoctor] = useState(null);
const [visualizacao, setVisualizacao] = useState('diario');
@@ -123,8 +131,51 @@ const ExcecoesDisponibilidade = () => {
fetchExcecoes(fromDate, toDate, filtroMedicoId);
}, [fetchExcecoes, filtroMedicoId, fromDate, toDate]);
+ useEffect(() => {
+ const fetchDoctors = async () => {
+ const myHeaders = new Headers();
+ const authHeader = resolveAuthHeader();
+ if (authHeader) myHeaders.append("Authorization", authHeader);
+ if (API_KEY) myHeaders.append("apikey", API_KEY);
+
+ try {
+ const response = await fetch('https://yuanqfswhberkoevtmfr.supabase.co/rest/v1/doctors?select=id,full_name', {
+ method: 'GET',
+ headers: myHeaders
+ });
+ if (response.ok) {
+ const doctors = await response.json();
+ setListaDeMedicos(doctors);
+ }
+ } catch (error) {
+ console.error('Erro ao buscar médicos:', error);
+ }
+ };
+ fetchDoctors();
+ }, []);
+
+ const handleSearchDoctors = (term) => {
+ setSearchTermDoctor(term);
+ if (term.trim() === '') {
+ setFilteredDoctors([]);
+ return;
+ }
+ const filtered = listaDeMedicos.filter(doc =>
+ doc.full_name.toLowerCase().includes(term.toLowerCase())
+ );
+ setFilteredDoctors(filtered);
+ };
+
+ const limparFiltros = () => {
+ setSearchTermDoctor('');
+ setFilteredDoctors([]);
+ setSelectedDoctor(null);
+ setFiltroMedicoId('');
+ setFiltroData(dayjs().format('YYYY-MM-DD'));
+ setVisualizacao('diario');
+ };
+
const deleteExcecao = async (id) => {
- if (!window.confirm("Confirma exclusão desta exceção?")) return;
const myHeaders = new Headers();
const authHeader = resolveAuthHeader();
if (authHeader) myHeaders.append("Authorization", authHeader);
@@ -139,6 +190,9 @@ const ExcecoesDisponibilidade = () => {
});
if (res.ok) {
setExcecoes(prev => prev.filter(x => x.id !== id));
+ setShowDeleteModal(false);
+ setSuccessMessage('Exceção excluída com sucesso!');
+ setShowSuccessModal(true);
} else {
const text = await res.text();
console.error('Erro ao deletar exceção', res.status, text);
@@ -163,7 +217,7 @@ const ExcecoesDisponibilidade = () => {
return (
- {/* Título e Botão de Criação */}
+
Gerenciar Exceções de Disponibilidade
{
-
+
+
+
+ Filtros
+
- {/* Filtros de Médico e Data */}
-
-
-
+
- {/* Botões de Visualização (Dia/Semana/Mês) */}
+
+
+ {selectedDoctor && (
+
+
+ {selectedDoctor.full_name}
+
+ )}
+
+ {excecoes.length} DE {excecoes.length} EXCEÇÕES ENCONTRADAS
+
+
+
+
+ Limpar Filtros
+
+
+
+
+
+
+
{
- {/* Tabela de Exceções (Título usa o titleRange calculado) */}
+
Exceções em {titleRange} ({excecoes.length})
@@ -264,7 +368,10 @@ const ExcecoesDisponibilidade = () => {
deleteExcecao(exc.id)}
+ onClick={() => {
+ setSelectedExceptionId(exc.id);
+ setShowDeleteModal(true);
+ }}
>
Excluir
@@ -278,6 +385,89 @@ const ExcecoesDisponibilidade = () => {
+
+ {showDeleteModal && (
+
+
+
+
+
+ Confirmação de Exclusão
+
+
+
+
+
+ Tem certeza que deseja excluir esta exceção?
+
+
+
+
+ setShowDeleteModal(false)}
+ >
+ Cancelar
+
+
+ deleteExcecao(selectedExceptionId)}
+ >
+ Excluir
+
+
+
+
+
+ )}
+
+
+ {showSuccessModal && (
+
+
+
+
+
+ Sucesso
+
+
+
+
+
+
+ setShowSuccessModal(false)}
+ >
+ OK
+
+
+
+
+
+ )}
);
}
diff --git a/src/pages/FinanceiroDashboard.jsx b/src/pages/FinanceiroDashboard.jsx
index b3768ce..a2c868d 100644
--- a/src/pages/FinanceiroDashboard.jsx
+++ b/src/pages/FinanceiroDashboard.jsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useMemo, useCallback } from "react";
-import './style/FinanceiroDashboard.css';
+import "./style/FinanceiroDashboard.css";
const CONVENIOS_LIST = [
"Particular",
@@ -8,72 +8,77 @@ const CONVENIOS_LIST = [
"SulAmérica",
"Unimed",
"Cassio",
- "Outro"
+ "Outro",
];
function CurrencyInput({ value, onChange, label, id }) {
const formattedValue = useMemo(() => {
let numericValue = Number(value) || 0;
-
+
let stringValue = String(numericValue);
while (stringValue.length < 3) {
- stringValue = '0' + stringValue;
+ stringValue = "0" + stringValue;
}
const integerPart = stringValue.slice(0, -2);
const decimalPart = stringValue.slice(-2);
-
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
return `R$ ${formattedInteger},${decimalPart}`;
}, [value]);
- const handleKeyDown = useCallback((e) => {
- const key = e.key;
+ const handleKeyDown = useCallback(
+ (e) => {
+ const key = e.key;
- if (['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Tab'].includes(key)) {
- if (key === 'Backspace' || key === 'Delete') {
+ if (
+ ["Backspace", "Delete", "ArrowLeft", "ArrowRight", "Tab"].includes(key)
+ ) {
+ if (key === "Backspace" || key === "Delete") {
e.preventDefault();
const numericValue = value || 0;
let newValueString = String(numericValue);
if (newValueString.length <= 1) {
- onChange(0);
+ onChange(0);
} else {
- const newNumericValue = parseInt(newValueString.slice(0, -1)) || 0;
- onChange(newNumericValue);
+ const newNumericValue = parseInt(newValueString.slice(0, -1)) || 0;
+ onChange(newNumericValue);
}
+ }
+ return;
+ }
+
+ if (!/^\d$/.test(key)) {
+ e.preventDefault();
+ return;
}
- return;
- }
- if (!/^\d$/.test(key)) {
e.preventDefault();
- return;
- }
-
- e.preventDefault();
-
- const digit = key;
- const numericValue = value || 0;
- let newValueString = String(numericValue) + digit;
-
- if (newValueString.length > 10) return;
+ const digit = key;
+ const numericValue = value || 0;
- const newNumericValue = parseInt(newValueString);
+ let newValueString = String(numericValue) + digit;
- onChange(newNumericValue);
- }, [value, onChange]);
+ if (newValueString.length > 10) return;
+
+ const newNumericValue = parseInt(newValueString);
+
+ onChange(newNumericValue);
+ },
+ [value, onChange]
+ );
return (
-
{}}
+ className="input-field currency-input"
+ type="text"
+ value={formattedValue}
+ onChange={() => {}}
onKeyDown={handleKeyDown}
placeholder="R$ 0,00"
/>
@@ -86,12 +91,12 @@ function mockFetchPagamentos() {
{
id: "PAY-001",
paciente: { nome: "Sarah Oliveira", convenio: "Unimed" },
- valor: 20000,
+ valor: 20000,
forma_pagamento: "Cartão",
data_vencimento: "2025-09-30",
status: "pendente",
desconto: 0,
- observacoes: "Pagamento parcelado em 2x"
+ observacoes: "Pagamento parcelado em 2x",
},
{
id: "PAY-002",
@@ -100,8 +105,8 @@ function mockFetchPagamentos() {
forma_pagamento: "Dinheiro",
data_vencimento: "2025-09-15",
status: "pago",
- desconto: 1000,
- observacoes: ""
+ desconto: 1000,
+ observacoes: "",
},
{
id: "PAY-003",
@@ -111,18 +116,18 @@ function mockFetchPagamentos() {
data_vencimento: "2025-09-20",
status: "vencido",
desconto: 0,
- observacoes: "Não respondeu ao contato"
+ observacoes: "Não respondeu ao contato",
},
- {
+ {
id: "PAY-004",
paciente: { nome: "Carlos Almeida", convenio: "Particular" },
valor: 10000,
forma_pagamento: "Transferência",
data_vencimento: "2025-09-29",
status: "pago",
- desconto: 500,
- observacoes: "Desconto por pagamento adiantado"
- }
+ desconto: 500,
+ observacoes: "Desconto por pagamento adiantado",
+ },
];
}
@@ -132,7 +137,11 @@ export default function FinanceiroDashboard() {
const [query, setQuery] = useState("");
const [filtroStatus, setFiltroStatus] = useState("Todos");
const [novoPagamento, setNovoPagamento] = useState(false);
- const [summary, setSummary] = useState({ totalRecebido: 0, totalAReceber: 0, totalDescontos: 0 });
+ const [summary, setSummary] = useState({
+ totalRecebido: 0,
+ totalAReceber: 0,
+ totalDescontos: 0,
+ });
useEffect(() => {
const data = mockFetchPagamentos();
@@ -141,7 +150,13 @@ export default function FinanceiroDashboard() {
function formatCurrency(centavos) {
const valorEmReais = centavos / 100;
- return "R$ " + valorEmReais.toFixed(2).replace(".", ",").replace(/\B(?=(\d{3})+(?!\d))/g, '.');
+ return (
+ "R$ " +
+ valorEmReais
+ .toFixed(2)
+ .replace(".", ",")
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ".")
+ );
}
function getValorLiquido(valor, desconto) {
@@ -149,11 +164,12 @@ export default function FinanceiroDashboard() {
}
const filteredPagamentos = useMemo(() => {
- return pagamentos.filter(p => {
+ return pagamentos.filter((p) => {
const q = query.toLowerCase();
const statusOk = filtroStatus === "Todos" || p.status === filtroStatus;
- const buscaOk = p.paciente.nome.toLowerCase().includes(q) ||
- p.id.toLowerCase().includes(q);
+ const buscaOk =
+ p.paciente.nome.toLowerCase().includes(q) ||
+ p.id.toLowerCase().includes(q);
return statusOk && buscaOk;
});
}, [pagamentos, query, filtroStatus]);
@@ -163,47 +179,55 @@ export default function FinanceiroDashboard() {
let aReceber = 0;
let descontos = 0;
- filteredPagamentos.forEach(p => {
+ filteredPagamentos.forEach((p) => {
const valorLiquido = getValorLiquido(p.valor, p.desconto);
- if (p.status === 'pago') {
+ if (p.status === "pago") {
recebido += valorLiquido;
descontos += p.desconto;
} else {
- aReceber += p.valor;
+ aReceber += valorLiquido;
}
});
setSummary({
totalRecebido: recebido,
totalAReceber: aReceber,
- totalDescontos: descontos
+ totalDescontos: descontos,
});
}, [filteredPagamentos]);
function handleDelete(id) {
if (window.confirm("Tem certeza que deseja excluir este pagamento?")) {
- setPagamentos(prev => prev.filter(p => p.id !== id));
+ setPagamentos((prev) => prev.filter((p) => p.id !== id));
setModalPagamento(null);
}
}
function handleSave(pagamento) {
- if (!pagamento.paciente.nome || !pagamento.valor || !pagamento.data_vencimento || !pagamento.paciente.convenio) {
- alert("Preencha Paciente, Convênio, Valor e Data de Vencimento.");
- return;
+ if (
+ !pagamento.paciente.nome ||
+ !pagamento.valor ||
+ !pagamento.data_vencimento ||
+ !pagamento.paciente.convenio
+ ) {
+ alert("Preencha Paciente, Convênio, Valor e Data de Vencimento.");
+ return;
}
if (novoPagamento) {
- const newId = "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0");
- pagamento.id = newId;
- setPagamentos(prev => [...prev, pagamento]);
+ const newId =
+ "PAY-" + (pagamentos.length + 1).toString().padStart(3, "0");
+ pagamento.id = newId;
+ setPagamentos((prev) => [...prev, pagamento]);
} else {
- setPagamentos(prev => prev.map(p => p.id === pagamento.id ? pagamento : p));
+ setPagamentos((prev) =>
+ prev.map((p) => (p.id === pagamento.id ? pagamento : p))
+ );
}
setModalPagamento(null);
setNovoPagamento(false);
}
-
+
const closeModal = () => {
setModalPagamento(null);
setNovoPagamento(false);
@@ -212,51 +236,54 @@ export default function FinanceiroDashboard() {
return (
Controle Financeiro
-
+
-
Total Recebido (Filtrado)
-
{formatCurrency(summary.totalRecebido)}
+
Total Recebido (Filtrado)
+
{formatCurrency(summary.totalRecebido)}
-
Total a Receber (Filtrado)
-
{formatCurrency(summary.totalAReceber)}
+
Total a Receber (Filtrado)
+
{formatCurrency(summary.totalAReceber)}
-
Descontos Aplicados
-
{formatCurrency(summary.totalDescontos)}
+
Descontos Aplicados
+
{formatCurrency(summary.totalDescontos)}
-
-
-
+
+
setQuery(e.target.value)}
+ placeholder="Buscar paciente"
+ value={query}
+ onChange={(e) => setQuery(e.target.value)}
style={{ flexGrow: 1 }}
/>
-