summaryrefslogtreecommitdiff
path: root/front-end
diff options
context:
space:
mode:
authorA Farzat <a@farzat.xyz>2025-10-09 09:51:14 +0300
committerA Farzat <a@farzat.xyz>2025-10-09 09:51:14 +0300
commit9fb20553d9f9e21c012f730a728c33d368e09bf2 (patch)
tree9299ef0ee912eaad281c1edb783b5c975c6fe0a2 /front-end
parent6e117b5438413d12cf84860ea5c8e3bfd1727f40 (diff)
downloadcsca5028-9fb20553d9f9e21c012f730a728c33d368e09bf2.tar.gz
csca5028-9fb20553d9f9e21c012f730a728c33d368e09bf2.zip
Add methods to modify/delete subscriptions
Diffstat (limited to 'front-end')
-rw-r--r--front-end/src/App.css127
-rw-r--r--front-end/src/App.jsx123
2 files changed, 250 insertions, 0 deletions
diff --git a/front-end/src/App.css b/front-end/src/App.css
index 20d839e..2808f4e 100644
--- a/front-end/src/App.css
+++ b/front-end/src/App.css
@@ -221,6 +221,133 @@ body {
margin-top: 0.3rem;
}
+.subscription-actions {
+ display: flex;
+ gap: 0.5rem;
+ margin-top: 0.5rem;
+ justify-content: center;
+}
+
+.edit-button, .delete-button {
+ padding: 0.4rem 0.8rem;
+ border: none;
+ border-radius: 10px;
+ font-size: 0.8rem;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.edit-button {
+ background: rgba(255, 193, 7, 0.2);
+ color: #ffc107;
+ border: 1px solid rgba(255, 193, 7, 0.3);
+}
+
+.edit-button:hover:not(:disabled) {
+ background: rgba(255, 193, 7, 0.3);
+ transform: translateY(-1px);
+}
+
+.delete-button {
+ background: rgba(220, 53, 69, 0.2);
+ color: #dc3545;
+ border: 1px solid rgba(220, 53, 69, 0.3);
+}
+
+.delete-button:hover:not(:disabled) {
+ background: rgba(220, 53, 69, 0.3);
+ transform: translateY(-1px);
+}
+
+.edit-button:disabled, .delete-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.edit-subscription-modal {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.7);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ z-index: 1000;
+ backdrop-filter: blur(5px);
+}
+
+.edit-subscription-content {
+ background: rgba(255, 255, 255, 0.15);
+ padding: 2rem;
+ border-radius: 15px;
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ max-width: 500px;
+ width: 90%;
+ max-height: 90vh;
+ overflow-y: auto;
+}
+
+.edit-subscription-content h3 {
+ color: white;
+ margin-bottom: 1.5rem;
+ text-align: center;
+ font-size: 1.3rem;
+}
+
+.cancel-button {
+ padding: 0.8rem 1.5rem;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ border-radius: 20px;
+ background: rgba(255, 255, 255, 0.1);
+ color: white;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ font-size: 1rem;
+ margin-left: 0.5rem;
+}
+
+.cancel-button:hover:not(:disabled) {
+ background: rgba(255, 255, 255, 0.2);
+ transform: translateY(-1px);
+}
+
+.cancel-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+/* Responsive updates for subscription actions */
+@media (max-width: 768px) {
+ .subscription-actions {
+ flex-direction: column;
+ align-items: center;
+ }
+
+ .edit-button, .delete-button {
+ width: 100%;
+ max-width: 200px;
+ }
+
+ .edit-subscription-content {
+ padding: 1.5rem;
+ margin: 1rem;
+ }
+
+ .form-actions {
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .cancel-button {
+ margin-left: 0;
+ margin-top: 0.5rem;
+ }
+}
+
.subscription-error {
color: #ff6b6b;
background: rgba(255, 107, 107, 0.1);
diff --git a/front-end/src/App.jsx b/front-end/src/App.jsx
index da6057e..1bdcdaf 100644
--- a/front-end/src/App.jsx
+++ b/front-end/src/App.jsx
@@ -19,6 +19,10 @@ function App() {
const [addingSubscription, setAddingSubscription] = useState(false);
const [subscriptionError, setSubscriptionError] = useState(null);
const [subscriptionSuccess, setSubscriptionSuccess] = useState(null);
+ const [editingSubscription, setEditingSubscription] = useState(null);
+ const [newTimeBetweenFetches, setNewTimeBetweenFetches] = useState(300);
+ const [updatingSubscription, setUpdatingSubscription] = useState(false);
+ const [deletingSubscription, setDeletingSubscription] = useState(false);
const fetchChannels = async () => {
try {
@@ -72,6 +76,57 @@ function App() {
}
};
+ const updateTimeBetweenFetches = async (subscriptionId, newTime) => {
+ try {
+ setUpdatingSubscription(true);
+ const formData = new FormData();
+ formData.append('_id', subscriptionId);
+ formData.append('time_between_fetches', newTime.toString());
+
+ await axios.post(`${API_BASE_URL}/set-time-between-fetches/`, formData, {
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ });
+
+ // Refresh the channels list to show updated data
+ fetchChannels();
+ setEditingSubscription(null);
+ setSubscriptionSuccess('Fetch interval updated successfully!');
+ } catch (err) {
+ console.error('Error updating fetch interval:', err);
+ setSubscriptionError('Failed to update fetch interval. Please try again.');
+ } finally {
+ setUpdatingSubscription(false);
+ }
+ };
+
+ const deleteSubscription = async (subscriptionId) => {
+ if (!window.confirm('Are you sure you want to delete this subscription? This action cannot be undone.')) {
+ return;
+ }
+
+ try {
+ setDeletingSubscription(true);
+ await axios.delete(`${API_BASE_URL}/delete-sub/${subscriptionId}`);
+
+ // If the deleted subscription was selected, clear the selection
+ if (selectedChannelId === subscriptionId.replace('yt:channel:', '').replace('yt:playlist:', '')) {
+ setSelectedChannelId('');
+ setVideos([]);
+ }
+
+ // Refresh the channels list
+ fetchChannels();
+ setSubscriptionSuccess('Subscription deleted successfully!');
+ } catch (err) {
+ console.error('Error deleting subscription:', err);
+ setSubscriptionError('Failed to delete subscription. Please try again.');
+ } finally {
+ setDeletingSubscription(false);
+ }
+ };
+
const fetchVideos = async (channelId) => {
try {
setLoading(true);
@@ -228,6 +283,16 @@ function App() {
setSubscriptionSuccess(null);
};
+ const startEditingSubscription = (subscription) => {
+ setEditingSubscription(subscription);
+ setNewTimeBetweenFetches(subscription.time_between_fetches);
+ };
+
+ const cancelEditing = () => {
+ setEditingSubscription(null);
+ setNewTimeBetweenFetches(300);
+ };
+
return (
<div className="App">
<header className="App-header">
@@ -286,9 +351,67 @@ function App() {
)}
</p>
)}
+ <div className="subscription-actions">
+ <button
+ onClick={() => startEditingSubscription(channels.find(ch => ch.id === selectedChannelId))}
+ className="edit-button"
+ >
+ ⚙️ Edit
+ </button>
+ <button
+ onClick={() => deleteSubscription(channels.find(ch => ch.id === selectedChannelId)?._id)}
+ className="delete-button"
+ disabled={deletingSubscription}
+ >
+ {deletingSubscription ? 'Deleting...' : '🗑️ Delete'}
+ </button>
+ </div>
</div>
)}
</div>
+
+ {editingSubscription && (
+ <div className="edit-subscription-modal">
+ <div className="edit-subscription-content">
+ <h3>Edit Subscription</h3>
+ <div className="form-group">
+ <label htmlFor="edit-time-between-fetches">
+ Fetch Interval (seconds):
+ </label>
+ <input
+ id="edit-time-between-fetches"
+ type="number"
+ value={newTimeBetweenFetches}
+ onChange={(e) => setNewTimeBetweenFetches(parseInt(e.target.value) || 0)}
+ min="60"
+ max="86400"
+ className="subscription-input"
+ disabled={updatingSubscription}
+ />
+ <small className="interval-help">
+ How often to check for new videos (current: {editingSubscription.time_between_fetches}s)
+ </small>
+ </div>
+ <div className="form-actions">
+ <button
+ onClick={() => updateTimeBetweenFetches(editingSubscription._id, newTimeBetweenFetches)}
+ className="submit-subscription"
+ disabled={updatingSubscription}
+ >
+ {updatingSubscription ? 'Updating...' : 'Update'}
+ </button>
+ <button
+ onClick={cancelEditing}
+ className="cancel-button"
+ disabled={updatingSubscription}
+ >
+ Cancel
+ </button>
+ </div>
+ </div>
+ </div>
+ )}
+
<div className="subscription-section">
<button
onClick={() => {