refactor: organize frontend by feature
Some checks failed
Backend CI/CD / build_and_deploy (push) Has been cancelled
Frontend CI/CD / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 01:05:50 -04:00
parent b6eb692c27
commit 121757546a
60 changed files with 107 additions and 183 deletions

View File

@@ -18,5 +18,7 @@
"vue",
"tailwindcss"
],
"rules": {}
"rules": {
"tailwindcss/no-custom-classname": "off"
}
}

View File

@@ -35,10 +35,10 @@
<script async setup>
import { computed, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
import AppBar from '@/views/main/AppBar.vue';
import AppSidebar from '@/views/main/AppSidebar.vue';
import AppBar from '@/layouts/main/AppBar.vue';
import AppSidebar from '@/layouts/main/AppSidebar.vue';
const route = useRoute();
const authStore = useAuthStore();

View File

@@ -1 +0,0 @@

View File

@@ -1,9 +0,0 @@
export const REACTIONS = {
LIKE: 'Like',
DISLIKE: 'Dislike',
LOVE: 'Love',
HAHA: 'Haha',
WOW: 'Wow',
SAD: 'Sad',
ANGRY: 'Angry'
};

View File

@@ -1 +0,0 @@

View File

@@ -1,6 +1,6 @@
import {onMounted, ref} from "vue";
import {useHead} from "@vueuse/head";
import {useAuthStore} from "@/stores/authStore.js";
import {useAuthStore} from "@/features/auth/stores/authStore.js";
import config from "@/config.js";
export function useFacebookLogin() {

View File

@@ -104,7 +104,7 @@
<script setup>
import { ref } from 'vue';
import { GoogleLogin } from 'vue3-google-login';
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { mdiEye, mdiEyeOff, mdiGoogle } from '@mdi/js';

View File

@@ -1,8 +1,8 @@
import { computed } from 'vue';
import { defineStore } from 'pinia';
import { useSessionStorage } from '@vueuse/core';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
export const useChannelsStore = defineStore('channels', () => {
const workspaceStore = useWorkspaceStore();

View File

@@ -2,9 +2,9 @@
import { computed, reactive, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useChannelsStore } from '@/stores/channelsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
import {
mdiClose,
mdiFacebook,

View File

@@ -1,7 +1,7 @@
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useClientsStore = defineStore('clients', () => {

View File

@@ -3,10 +3,10 @@
import { useRoute } from 'vue-router';
import AppAvatar from '@/components/AppAvatar.vue';
import ImageCropperDialog from '@/components/ImageCropperDialog.vue';
import { useAuthStore } from '@/stores/authStore.js';
import { useClientsStore } from '@/stores/clientsStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
const authStore = useAuthStore();
const route = useRoute();

View File

@@ -2,9 +2,9 @@
import { reactive, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import AppAvatar from '@/components/AppAvatar.vue';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useClientsStore } from '@/stores/clientsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
const authStore = useAuthStore();
const workspaceStore = useWorkspaceStore();

View File

@@ -1,6 +1,6 @@
import { reactive, ref } from 'vue';
import { defineStore } from 'pinia';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useContentItemDetailStore = defineStore('content-item-detail', () => {

View File

@@ -1,7 +1,7 @@
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useContentItemsStore = defineStore('content-items', () => {

View File

@@ -3,12 +3,12 @@
import { useRoute, useRouter } from 'vue-router';
import { useSessionStorage } from '@vueuse/core';
import AppAvatar from '@/components/AppAvatar.vue';
import { useChannelsStore } from '@/stores/channelsStore.js';
import { useClientsStore } from '@/stores/clientsStore.js';
import { useContentItemDetailStore } from '@/stores/contentItemDetailStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
import { useContentItemDetailStore } from '@/features/content/stores/contentItemDetailStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
const route = useRoute();
const router = useRouter();

View File

@@ -1,7 +1,7 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/stores/authStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
const { t } = useI18n();
const authStore = useAuthStore();

View File

@@ -1,7 +1,7 @@
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useNotificationsStore = defineStore('notifications', () => {

View File

@@ -1,7 +1,7 @@
import { ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
export const useProjectsStore = defineStore('projects', () => {

View File

@@ -1,10 +1,10 @@
<script setup>
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
const authStore = useAuthStore();
const route = useRoute();

View File

@@ -2,10 +2,10 @@
import { computed, reactive, ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/stores/authStore.js';
import { useClientsStore } from '@/stores/clientsStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
const route = useRoute();
const authStore = useAuthStore();

View File

@@ -1,7 +1,7 @@
import { computed } from 'vue';
import { defineStore } from 'pinia';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
const stageByStatus = {
Draft: 'Draft',

View File

@@ -1,6 +1,6 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useReviewQueueStore } from '@/stores/reviewQueueStore.js';
import { useReviewQueueStore } from '@/features/reviews/stores/reviewQueueStore.js';
const { t } = useI18n();
const reviewQueueStore = useReviewQueueStore();

View File

@@ -1,6 +1,6 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
const authStore = useAuthStore();
const { t } = useI18n();

View File

@@ -1,6 +1,6 @@
import {computed, watch} from 'vue'
import {defineStore} from 'pinia'
import {useAuthStore} from "@/stores/authStore.js";
import {useAuthStore} from "@/features/auth/stores/authStore.js";
import {useClient} from "@/plugins/api.js";
import {useSessionStorage} from "@vueuse/core";

View File

@@ -3,7 +3,7 @@
import { useI18n } from 'vue-i18n';
import AppAvatar from '@/components/AppAvatar.vue';
import ImageCropperDialog from '@/components/ImageCropperDialog.vue';
import { useUserProfileStore } from '@/stores/userProfileStore.js';
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
const userProfileStore = useUserProfileStore();
const { t } = useI18n();

View File

@@ -1,6 +1,6 @@
import { computed, ref, watch } from 'vue';
import { defineStore } from 'pinia';
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useClient } from '@/plugins/api.js';
export const useWorkspaceStore = defineStore('workspace', () => {

View File

@@ -2,9 +2,9 @@
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { mdiChevronLeft, mdiChevronRight } from '@mdi/js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
const { t, locale } = useI18n();
const workspaceStore = useWorkspaceStore();

View File

@@ -1,8 +1,8 @@
<script setup>
import { computed, onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useClient } from '@/plugins/api.js';
const { locale, t } = useI18n();

View File

@@ -2,7 +2,7 @@
import { computed, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
const router = useRouter();
const { t } = useI18n();

View File

@@ -1,7 +1,7 @@
<script setup>
import { computed, reactive, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import {
mdiAccountGroupOutline,
mdiCheckCircleOutline,

View File

@@ -1 +0,0 @@

View File

@@ -2,8 +2,8 @@
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useAuthStore } from '@/stores/authStore.js';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import {
mdiChevronDown,
mdiCogOutline,

View File

@@ -3,13 +3,13 @@
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import AppAvatar from '@/components/AppAvatar.vue';
import { useAuthStore } from '@/stores/authStore.js';
import { useChannelsStore } from '@/stores/channelsStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
import { useLanguageStore } from '@/stores/languageStore.js';
import { useNotificationsStore } from '@/stores/notificationsStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useUserProfileStore } from '@/stores/userProfileStore.js';
import { useNotificationsStore } from '@/features/notifications/stores/notificationsStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
import {
mdiBellOutline,
mdiCalendarMonthOutline,

View File

@@ -19,18 +19,18 @@ import {
VTextField,
} from 'vuetify/components';
import vueGoogleOauth from 'vue3-google-login';
import { useAuthStore } from '@/stores/authStore.js';
import { useUserProfileStore } from '@/stores/userProfileStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { useUserProfileStore } from '@/features/user-profile/stores/userProfileStore.js';
import Toast, { POSITION } from 'vue-toastification';
import 'vue-toastification/dist/index.css';
import './assets/main.css';
import { useWorkspaceStore } from '@/stores/workspaceStore.js';
import { useReviewQueueStore } from '@/stores/reviewQueueStore.js';
import { useContentItemsStore } from '@/stores/contentItemsStore.js';
import { useClientsStore } from '@/stores/clientsStore.js';
import { useProjectsStore } from '@/stores/projectsStore.js';
import { useNotificationsStore } from '@/stores/notificationsStore.js';
import { useChannelsStore } from '@/stores/channelsStore.js';
import { useWorkspaceStore } from '@/features/workspaces/stores/workspaceStore.js';
import { useReviewQueueStore } from '@/features/reviews/stores/reviewQueueStore.js';
import { useContentItemsStore } from '@/features/content/stores/contentItemsStore.js';
import { useClientsStore } from '@/features/clients/stores/clientsStore.js';
import { useProjectsStore } from '@/features/projects/stores/projectsStore.js';
import { useNotificationsStore } from '@/features/notifications/stores/notificationsStore.js';
import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
import { i18n } from '@/plugins/i18n.js';
import config from '@/config.js';

View File

@@ -1,5 +1,5 @@
import axios from 'axios';
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import config from '@/config.js';
export function useClient() {

View File

@@ -1,26 +1,26 @@
import { useAuthStore } from '@/stores/authStore.js';
import { useAuthStore } from '@/features/auth/stores/authStore.js';
import { createRouter, createWebHistory } from 'vue-router';
const LoginView = () => import('@/views/auth/LoginView.vue');
const Landing = () => import('@/views/main/Landing.vue');
const RegisterView = () => import('@/views/auth/RegisterView.vue');
const ForgotPasswordView = () => import('@/views/auth/ForgotPasswordView.vue');
const ResetPasswordView = () => import('@/views/auth/ResetPasswordView.vue');
const VerifyEmailView = () => import('@/views/auth/VerifyEmailView.vue');
const OverviewView = () => import('@/views/app/OverviewView.vue');
const DashboardView = () => import('@/views/app/DashboardView.vue');
const ChannelsView = () => import('@/views/app/ChannelsView.vue');
const CampaignsView = () => import('@/views/app/ProjectsView.vue');
const CampaignDetailView = () => import('@/views/app/ProjectDetailView.vue');
const MediaLibraryView = () => import('@/views/app/MediaLibraryView.vue');
const WorkspaceCreateView = () => import('@/views/app/WorkspaceCreateView.vue');
const SettingsLayoutView = () => import('@/views/app/SettingsLayoutView.vue');
const UserSettingsView = () => import('@/views/app/UserSettingsView.vue');
const IntegrationsSettingsView = () => import('@/views/app/IntegrationsSettingsView.vue');
const WorkspaceSettingsView = () => import('@/views/app/WorkspaceSettingsView.vue');
const ReviewQueueView = () => import('@/views/app/ReviewQueueView.vue');
const ContentItemsView = () => import('@/views/app/ContentItemsView.vue');
const ContentItemDetailView = () => import('@/views/app/ContentItemDetailView.vue');
const LoginView = () => import('@/features/auth/views/LoginView.vue');
const Landing = () => import('@/features/landing/views/Landing.vue');
const RegisterView = () => import('@/features/auth/views/RegisterView.vue');
const ForgotPasswordView = () => import('@/features/auth/views/ForgotPasswordView.vue');
const ResetPasswordView = () => import('@/features/auth/views/ResetPasswordView.vue');
const VerifyEmailView = () => import('@/features/auth/views/VerifyEmailView.vue');
const OverviewView = () => import('@/features/workspaces/views/OverviewView.vue');
const DashboardView = () => import('@/features/workspaces/views/DashboardView.vue');
const ChannelsView = () => import('@/features/channels/views/ChannelsView.vue');
const CampaignsView = () => import('@/features/projects/views/ProjectsView.vue');
const CampaignDetailView = () => import('@/features/projects/views/ProjectDetailView.vue');
const MediaLibraryView = () => import('@/features/content/views/MediaLibraryView.vue');
const WorkspaceCreateView = () => import('@/features/workspaces/views/WorkspaceCreateView.vue');
const SettingsLayoutView = () => import('@/features/settings/views/SettingsLayoutView.vue');
const UserSettingsView = () => import('@/features/user-profile/views/UserSettingsView.vue');
const IntegrationsSettingsView = () => import('@/features/settings/views/IntegrationsSettingsView.vue');
const WorkspaceSettingsView = () => import('@/features/workspaces/views/WorkspaceSettingsView.vue');
const ReviewQueueView = () => import('@/features/reviews/views/ReviewQueueView.vue');
const ContentItemsView = () => import('@/features/content/views/ContentItemsView.vue');
const ContentItemDetailView = () => import('@/features/content/views/ContentItemDetailView.vue');
const routes = [
{

View File

@@ -1,64 +0,0 @@
/**
* Regular expression for matching YouTube video IDs in various URL formats
*/
const VIDEO_ID_REGEX = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i;
/**
* Regular expression for matching standalone YouTube video IDs
*/
const SHORT_URL_REGEX = /^[a-zA-Z0-9_-]{11}$/;
/**
* Extracts the video ID from a YouTube URL or returns the input if it's already a video ID.
* @param {string} input - The YouTube URL or video ID
* @returns {string|null} The extracted video ID or null if invalid
*/
export function extractVideoId(input) {
if (!input) return null;
// If it's already a valid video ID, return it
if (isValidVideoId(input)) {
return input;
}
// Try to extract video ID from URL
const match = input.match(VIDEO_ID_REGEX);
return match ? match[1] : null;
}
/**
* Validates if the input is a valid YouTube video ID.
* @param {string} input - The video ID to validate
* @returns {boolean} True if the input is a valid video ID
*/
export function isValidVideoId(input) {
if (!input) return false;
return SHORT_URL_REGEX.test(input);
}
/**
* Validates if the input is a valid YouTube URL or video ID.
* @param {string} input - The URL or video ID to validate
* @returns {boolean} True if the input is a valid YouTube URL or video ID
*/
export function isValidYouTubeUrlOrId(input) {
if (!input) return false;
// Check if it's a valid video ID
if (isValidVideoId(input)) {
return true;
}
// Check if it's a valid YouTube URL
return VIDEO_ID_REGEX.test(input);
}
/**
* Builds a YouTube embed URL from a video ID.
* @param {string} videoId - The YouTube video ID
* @returns {string} The YouTube embed URL
*/
export function buildEmbedUrl(videoId) {
if (!videoId) return '';
return `https://www.youtube.com/embed/${videoId}`;
}