Merge pull request #7 from kappa118/main
React UI is live! and other changes...
|
|
@ -66,6 +66,13 @@ class ChannelSerializer(serializers.ModelSerializer):
|
|||
required=False
|
||||
)
|
||||
|
||||
stream_profile_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=StreamProfile.objects.all(),
|
||||
source='stream_profile',
|
||||
allow_null=True,
|
||||
required=False
|
||||
)
|
||||
|
||||
# Possibly show streams inline, or just by ID
|
||||
# streams = StreamSerializer(many=True, read_only=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,8 +50,10 @@ class EPGGridAPIView(APIView):
|
|||
responses={200: ProgramDataSerializer(many=True)}
|
||||
)
|
||||
def get(self, request, format=None):
|
||||
now = timezone.now()
|
||||
twelve_hours_later = now + timedelta(hours=12)
|
||||
# Get current date and reset time to midnight (00:00)
|
||||
now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
twelve_hours_later = now + timedelta(hours=24)
|
||||
logger.debug(f"EPGGridAPIView: Querying programs between {now} and {twelve_hours_later}.")
|
||||
# Use select_related to prefetch EPGData and Channel data
|
||||
programs = ProgramData.objects.select_related('epg__channel').filter(
|
||||
|
|
|
|||
|
|
@ -49,7 +49,10 @@ ROOT_URLCONF = 'dispatcharr.urls'
|
|||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates'],
|
||||
'DIRS': [
|
||||
os.path.join(BASE_DIR, 'frontend/build'),
|
||||
BASE_DIR / "templates"
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
|
|
@ -98,8 +101,14 @@ USE_I18N = True
|
|||
USE_TZ = True
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||
STATICFILES_DIRS = [BASE_DIR / 'static']
|
||||
STATIC_ROOT = BASE_DIR / 'staticfiles' # Directory where static files will be collected
|
||||
|
||||
# Adjust STATICFILES_DIRS to include the paths to the directories that contain your static files.
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'frontend/build/static'), # React build static files
|
||||
BASE_DIR / 'static', # Django custom static files (if any)
|
||||
]
|
||||
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||
AUTH_USER_MODEL = 'accounts.User'
|
||||
|
|
@ -116,16 +125,15 @@ SERVER_IP = "127.0.0.1"
|
|||
CORS_ALLOW_ALL_ORIGINS = True
|
||||
CORS_ALLOW_CREDENTIALS = True
|
||||
|
||||
if os.getenv('REACT_UI', False):
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
],
|
||||
}
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||
'rest_framework_simplejwt.authentication.JWTAuthentication',
|
||||
],
|
||||
}
|
||||
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||
'ROTATE_REFRESH_TOKENS': False, # Optional: Whether to rotate refresh tokens
|
||||
'BLACKLIST_AFTER_ROTATION': True, # Optional: Whether to blacklist refresh tokens
|
||||
}
|
||||
SIMPLE_JWT = {
|
||||
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=30),
|
||||
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
|
||||
'ROTATE_REFRESH_TOKENS': False, # Optional: Whether to rotate refresh tokens
|
||||
'BLACKLIST_AFTER_ROTATION': True, # Optional: Whether to blacklist refresh tokens
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from django.contrib import admin
|
|||
from django.urls import path, include
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.views.generic import RedirectView
|
||||
from django.views.generic import TemplateView
|
||||
from rest_framework import permissions
|
||||
from drf_yasg.views import get_schema_view
|
||||
from drf_yasg import openapi
|
||||
|
|
@ -21,39 +21,30 @@ schema_view = get_schema_view(
|
|||
permission_classes=(permissions.AllowAny,),
|
||||
)
|
||||
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', RedirectView.as_view(pattern_name='dashboard:dashboard'), name='home'),
|
||||
# API Routes
|
||||
path('api/', include(('apps.api.urls', 'api'), namespace='api')),
|
||||
|
||||
# Admin
|
||||
path('admin/', admin.site.urls),
|
||||
|
||||
path('accounts/', include(('apps.accounts.urls', 'accounts'), namespace='accounts')),
|
||||
#path('streams/', include(('apps.streams.urls', 'streams'), namespace='streams')),
|
||||
#path('hdhr/', include(('apps.hdhr.urls', 'hdhr'), namespace='hdhr')),
|
||||
path('m3u/', include(('apps.m3u.urls', 'm3u'), namespace='m3u')),
|
||||
path('epg/', include(('apps.epg.urls', 'epg'), namespace='epg')),
|
||||
path('channels/', include(('apps.channels.urls', 'channels'), namespace='channels')),
|
||||
path('settings/', include(('core.urls', 'settings'), namespace='settings')),
|
||||
#path('backup/', include(('apps.backup.urls', 'backup'), namespace='backup')),
|
||||
path('dashboard/', include(('apps.dashboard.urls', 'dashboard'), namespace='dashboard')),
|
||||
path('output/', include('apps.output.urls', namespace='output')),
|
||||
|
||||
|
||||
# Swagger UI:
|
||||
# Swagger UI
|
||||
path('swagger/', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
|
||||
# ReDoc UI:
|
||||
|
||||
# ReDoc UI
|
||||
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
|
||||
# Optionally, you can also serve the raw JSON:
|
||||
|
||||
# Optionally, serve the raw Swagger JSON
|
||||
path('swagger.json', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||
|
||||
# Catch-all route to serve React's index.html for non-API, non-admin paths
|
||||
path('', TemplateView.as_view(template_name='index.html')), # React entry point
|
||||
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
# Serve static files for development (React's JS, CSS, etc.)
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
urlpatterns += static(
|
||||
settings.MEDIA_URL,
|
||||
document_root=settings.MEDIA_ROOT
|
||||
)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
||||
urlpatterns += [path('<path:unused_path>', TemplateView.as_view(template_name='index.html'))]
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ services:
|
|||
container_name: dispatcharr_web
|
||||
ports:
|
||||
- "9191:9191"
|
||||
- "3031:3031"
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
|
|
@ -25,11 +26,10 @@ services:
|
|||
ui:
|
||||
image: alpine
|
||||
container_name: dispatcharr_ui
|
||||
network_mode: service:web
|
||||
volumes:
|
||||
- ../frontend:/app
|
||||
entrypoint: ["/bin/sh", "/app/entrypoint.sh"]
|
||||
ports:
|
||||
- 3031:3031
|
||||
|
||||
celery:
|
||||
build:
|
||||
|
|
|
|||
31
frontend/package-lock.json
generated
|
|
@ -14,6 +14,7 @@
|
|||
"@mui/icons-material": "^6.4.5",
|
||||
"@mui/material": "^6.4.5",
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
"eslint": "^8.57.1",
|
||||
"formik": "^2.4.6",
|
||||
"material-react-table": "^3.2.0",
|
||||
|
|
@ -23,6 +24,7 @@
|
|||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-window": "^1.8.11",
|
||||
"web-vitals": "^2.1.4",
|
||||
"yup": "^1.6.1",
|
||||
"zustand": "^5.0.3"
|
||||
|
|
@ -6511,6 +6513,12 @@
|
|||
"url": "https://opencollective.com/date-fns"
|
||||
}
|
||||
},
|
||||
"node_modules/dayjs": {
|
||||
"version": "1.11.13",
|
||||
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz",
|
||||
"integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
|
|
@ -11183,6 +11191,12 @@
|
|||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
|
||||
|
|
@ -13783,6 +13797,23 @@
|
|||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-window": {
|
||||
"version": "1.8.11",
|
||||
"resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.11.tgz",
|
||||
"integrity": "sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"memoize-one": ">=3.1.1 <6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">8.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
"@mui/icons-material": "^6.4.5",
|
||||
"@mui/material": "^6.4.5",
|
||||
"axios": "^1.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
"eslint": "^8.57.1",
|
||||
"formik": "^2.4.6",
|
||||
"material-react-table": "^3.2.0",
|
||||
|
|
@ -18,6 +19,7 @@
|
|||
"react-dom": "^19.0.0",
|
||||
"react-router-dom": "^7.2.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-window": "^1.8.11",
|
||||
"web-vitals": "^2.1.4",
|
||||
"yup": "^1.6.1",
|
||||
"zustand": "^5.0.3"
|
||||
|
|
@ -46,5 +48,6 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
},
|
||||
"proxy": "http://127.0.0.1:9191"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="%PUBLIC_URL%/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="%PUBLIC_URL%/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="%PUBLIC_URL%/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ import Channels from './pages/Channels';
|
|||
import M3U from './pages/M3U';
|
||||
import { ThemeProvider } from '@mui/material/styles'; // Import theme tools
|
||||
import {
|
||||
AppBar,
|
||||
Toolbar,
|
||||
Typography,
|
||||
Box,
|
||||
CssBaseline,
|
||||
Drawer,
|
||||
|
|
@ -28,7 +25,7 @@ import EPG from './pages/EPG';
|
|||
import Guide from './pages/Guide';
|
||||
import StreamProfiles from './pages/StreamProfiles';
|
||||
import useAuthStore from './store/auth';
|
||||
import API from './api';
|
||||
import logo from './images/logo.png';
|
||||
|
||||
const drawerWidth = 240;
|
||||
const miniDrawerWidth = 60;
|
||||
|
|
@ -104,7 +101,7 @@ const App = () => {
|
|||
pb: 0,
|
||||
}}
|
||||
>
|
||||
<img src="/images/logo.png" width="33x" />
|
||||
<img src={logo} width="33x" />
|
||||
{open && (
|
||||
<ListItemText primary="Dispatcharr" sx={{ paddingLeft: 3 }} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import Axios from 'axios';
|
||||
import useAuthStore from './store/auth';
|
||||
import useChannelsStore from './store/channels';
|
||||
import useUserAgentsStore from './store/userAgents';
|
||||
|
|
@ -11,7 +10,7 @@ import useStreamProfilesStore from './store/streamProfiles';
|
|||
// withCredentials: true,
|
||||
// });
|
||||
|
||||
const host = 'http://192.168.1.151:9191';
|
||||
const host = '';
|
||||
|
||||
const getAuthToken = async () => {
|
||||
const token = await useAuthStore.getState().getToken(); // Assuming token is stored in Zustand store
|
||||
|
|
@ -602,7 +601,6 @@ export default class API {
|
|||
});
|
||||
|
||||
const retval = await response.json();
|
||||
console.log(retval);
|
||||
return retval;
|
||||
return retval.data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
} from 'material-react-table';
|
||||
import ChannelGroupForm from './ChannelGroup';
|
||||
import usePlaylistsStore from '../../store/playlists';
|
||||
import logo from '../../images/logo.png';
|
||||
|
||||
const Channel = ({ channel = null, isOpen, onClose }) => {
|
||||
const channelGroups = useChannelsStore((state) => state.channelGroups);
|
||||
|
|
@ -38,8 +39,8 @@ const Channel = ({ channel = null, isOpen, onClose }) => {
|
|||
const { profiles: streamProfiles } = useStreamProfilesStore();
|
||||
const { playlists } = usePlaylistsStore();
|
||||
|
||||
const [logo, setLogo] = useState(null);
|
||||
const [logoPreview, setLogoPreview] = useState('/images/logo.png');
|
||||
const [logoFile, setLogoFile] = useState(null);
|
||||
const [logoPreview, setLogoPreview] = useState(logo);
|
||||
const [channelStreams, setChannelStreams] = useState([]);
|
||||
const [channelGroupModelOpen, setChannelGroupModalOpen] = useState(false);
|
||||
|
||||
|
|
@ -58,7 +59,7 @@ const Channel = ({ channel = null, isOpen, onClose }) => {
|
|||
const handleLogoChange = (e) => {
|
||||
const file = e.target.files[0];
|
||||
if (file) {
|
||||
setLogo(file);
|
||||
setLogoFile(file);
|
||||
setLogoPreview(URL.createObjectURL(file));
|
||||
}
|
||||
};
|
||||
|
|
@ -83,20 +84,20 @@ const Channel = ({ channel = null, isOpen, onClose }) => {
|
|||
await API.updateChannel({
|
||||
id: channel.id,
|
||||
...values,
|
||||
logo_file: logo,
|
||||
logo_file: logoFile,
|
||||
streams: channelStreams.map((stream) => stream.id),
|
||||
});
|
||||
} else {
|
||||
await API.addChannel({
|
||||
...values,
|
||||
logo_file: logo,
|
||||
logo_file: logoFile,
|
||||
streams: channelStreams.map((stream) => stream.id),
|
||||
});
|
||||
}
|
||||
|
||||
resetForm();
|
||||
setLogo(null);
|
||||
setLogoPreview('/images/logo.png');
|
||||
setLogoFile(null);
|
||||
setLogoPreview(logo);
|
||||
setSubmitting(false);
|
||||
onClose();
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ const Stream = ({ stream = null, isOpen, onClose }) => {
|
|||
validationSchema: Yup.object({
|
||||
name: Yup.string().required('Name is required'),
|
||||
url: Yup.string().required('URL is required').min(0),
|
||||
stream_profile_id: Yup.string().required('Stream profile is required'),
|
||||
// stream_profile_id: Yup.string().required('Stream profile is required'),
|
||||
}),
|
||||
onSubmit: async (values, { setSubmitting, resetForm }) => {
|
||||
if (stream?.id) {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import ChannelForm from '../forms/Channel';
|
|||
import { TableHelper } from '../../helpers';
|
||||
import utils from '../../utils';
|
||||
import { ContentCopy } from '@mui/icons-material';
|
||||
import logo from '../../images/logo.png';
|
||||
|
||||
const Example = () => {
|
||||
const [channel, setChannel] = useState(null);
|
||||
|
|
@ -71,7 +72,7 @@ const Example = () => {
|
|||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<img src={info.getValue() || '/images/logo.png'} width="20" />
|
||||
<img src={info.getValue() || logo} width="20" />
|
||||
</Grid2>
|
||||
),
|
||||
meta: {
|
||||
|
|
@ -175,7 +176,6 @@ const Example = () => {
|
|||
...TableHelper.defaultProperties,
|
||||
columns,
|
||||
data: channels,
|
||||
// enableGlobalFilterModes: true,
|
||||
enablePagination: false,
|
||||
// enableRowNumbers: true,
|
||||
enableRowVirtualization: true,
|
||||
|
|
@ -194,13 +194,14 @@ const Example = () => {
|
|||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
<>
|
||||
<Box sx={{ justifyContent: 'right' }}>
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="warning" // Red color for delete actions
|
||||
onClick={() => {
|
||||
editChannel(row.original);
|
||||
}}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -208,14 +209,15 @@ const Example = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deleteChannel(row.original.id)}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
</>
|
||||
</Box>
|
||||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: 'calc(100vh - 90px)', // Subtract padding to avoid cutoff
|
||||
height: 'calc(100vh - 75px)', // Subtract padding to avoid cutoff
|
||||
overflowY: 'auto', // Internal scrolling for the table
|
||||
},
|
||||
},
|
||||
|
|
@ -266,13 +268,13 @@ const Example = () => {
|
|||
marginLeft: 1,
|
||||
}}
|
||||
>
|
||||
<Button variant="contained" onClick={copyHDHRUrl}>
|
||||
<Button variant="contained" size="small" onClick={copyHDHRUrl}>
|
||||
HDHR URL
|
||||
</Button>
|
||||
<Button variant="contained" onClick={copyM3UUrl}>
|
||||
<Button variant="contained" size="small" onClick={copyM3UUrl}>
|
||||
M3U URL
|
||||
</Button>
|
||||
<Button variant="contained" onClick={copyEPGUrl}>
|
||||
<Button variant="contained" size="small" onClick={copyEPGUrl}>
|
||||
EPG
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
MaterialReactTable,
|
||||
MRT_ShowHideColumnsButton,
|
||||
MRT_ToggleFullScreenButton,
|
||||
useMaterialReactTable,
|
||||
} from "material-react-table";
|
||||
} from 'material-react-table';
|
||||
import {
|
||||
Box,
|
||||
Grid2,
|
||||
|
|
@ -16,8 +16,8 @@ import {
|
|||
Select,
|
||||
MenuItem,
|
||||
Snackbar,
|
||||
} from "@mui/material";
|
||||
import API from "../../api";
|
||||
} from '@mui/material';
|
||||
import API from '../../api';
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
Edit as EditIcon,
|
||||
|
|
@ -26,16 +26,16 @@ import {
|
|||
Check as CheckIcon,
|
||||
Close as CloseIcon,
|
||||
Refresh as RefreshIcon,
|
||||
} from "@mui/icons-material";
|
||||
import useEPGsStore from "../../store/epgs";
|
||||
import EPGForm from "../forms/EPG";
|
||||
import { TableHelper } from "../../helpers";
|
||||
} from '@mui/icons-material';
|
||||
import useEPGsStore from '../../store/epgs';
|
||||
import EPGForm from '../forms/EPG';
|
||||
import { TableHelper } from '../../helpers';
|
||||
|
||||
const EPGsTable = () => {
|
||||
const [epg, setEPG] = useState(null);
|
||||
const [epgModalOpen, setEPGModalOpen] = useState(false);
|
||||
const [rowSelection, setRowSelection] = useState([]);
|
||||
const [snackbarMessage, setSnackbarMessage] = useState("");
|
||||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||||
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||||
|
||||
const epgs = useEPGsStore((state) => state.epgs);
|
||||
|
|
@ -44,21 +44,19 @@ const EPGsTable = () => {
|
|||
//column definitions...
|
||||
() => [
|
||||
{
|
||||
header: "Name",
|
||||
size: 10,
|
||||
accessorKey: "name",
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
},
|
||||
{
|
||||
header: "Source Type",
|
||||
accessorKey: "source_type",
|
||||
size: 50,
|
||||
header: 'Source Type',
|
||||
accessorKey: 'source_type',
|
||||
},
|
||||
{
|
||||
header: "URL / API Key",
|
||||
accessorKey: "max_streams",
|
||||
header: 'URL / API Key',
|
||||
accessorKey: 'max_streams',
|
||||
},
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
//optionally access the underlying virtualizer instance
|
||||
|
|
@ -82,12 +80,12 @@ const EPGsTable = () => {
|
|||
|
||||
const refreshEPG = async (id) => {
|
||||
await API.refreshEPG(id);
|
||||
setSnackbarMessage("EPG refresh initiated");
|
||||
setSnackbarMessage('EPG refresh initiated');
|
||||
setSnackbarOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
|
@ -118,7 +116,7 @@ const EPGsTable = () => {
|
|||
rowVirtualizerInstanceRef, //optional
|
||||
rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
|
||||
initialState: {
|
||||
density: "compact",
|
||||
density: 'compact',
|
||||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
|
|
@ -127,6 +125,7 @@ const EPGsTable = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="info" // Red color for delete actions
|
||||
onClick={() => editEPG(row.original)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -134,6 +133,7 @@ const EPGsTable = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deleteEPG(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -141,6 +141,7 @@ const EPGsTable = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="info" // Red color for delete actions
|
||||
onClick={() => refreshEPG(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<RefreshIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -148,14 +149,14 @@ const EPGsTable = () => {
|
|||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: "calc(42vh - 0px)",
|
||||
height: 'calc(43vh - 0px)',
|
||||
},
|
||||
},
|
||||
renderTopToolbarCustomActions: ({ table }) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>EPGs</Typography>
|
||||
|
|
@ -176,7 +177,7 @@ const EPGsTable = () => {
|
|||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: 2,
|
||||
padding: 1,
|
||||
}}
|
||||
>
|
||||
<MaterialReactTable table={table} />
|
||||
|
|
@ -188,7 +189,7 @@ const EPGsTable = () => {
|
|||
/>
|
||||
|
||||
<Snackbar
|
||||
anchorOrigin={{ vertical: "top", horizontal: "right" }}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'right' }}
|
||||
open={snackbarOpen}
|
||||
autoHideDuration={5000}
|
||||
onClose={closeSnackbar}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
MaterialReactTable,
|
||||
MRT_ShowHideColumnsButton,
|
||||
MRT_ToggleFullScreenButton,
|
||||
useMaterialReactTable,
|
||||
} from "material-react-table";
|
||||
} from 'material-react-table';
|
||||
import {
|
||||
Box,
|
||||
Grid2,
|
||||
|
|
@ -15,8 +15,8 @@ import {
|
|||
Checkbox,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@mui/material";
|
||||
import API from "../../api";
|
||||
} from '@mui/material';
|
||||
import API from '../../api';
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
Edit as EditIcon,
|
||||
|
|
@ -25,16 +25,16 @@ import {
|
|||
Check as CheckIcon,
|
||||
Close as CloseIcon,
|
||||
Refresh as RefreshIcon,
|
||||
} from "@mui/icons-material";
|
||||
import usePlaylistsStore from "../../store/playlists";
|
||||
import M3UForm from "../forms/M3U";
|
||||
import { TableHelper } from "../../helpers";
|
||||
} from '@mui/icons-material';
|
||||
import usePlaylistsStore from '../../store/playlists';
|
||||
import M3UForm from '../forms/M3U';
|
||||
import { TableHelper } from '../../helpers';
|
||||
|
||||
const Example = () => {
|
||||
const [playlist, setPlaylist] = useState(null);
|
||||
const [playlistModalOpen, setPlaylistModalOpen] = useState(false);
|
||||
const [rowSelection, setRowSelection] = useState([]);
|
||||
const [activeFilterValue, setActiveFilterValue] = useState("all");
|
||||
const [activeFilterValue, setActiveFilterValue] = useState('all');
|
||||
|
||||
const playlists = usePlaylistsStore((state) => state.playlists);
|
||||
|
||||
|
|
@ -42,28 +42,28 @@ const Example = () => {
|
|||
//column definitions...
|
||||
() => [
|
||||
{
|
||||
header: "Name",
|
||||
accessorKey: "name",
|
||||
header: 'Name',
|
||||
accessorKey: 'name',
|
||||
},
|
||||
{
|
||||
header: "URL / File",
|
||||
accessorKey: "server_url",
|
||||
header: 'URL / File',
|
||||
accessorKey: 'server_url',
|
||||
},
|
||||
{
|
||||
header: "Max Streams",
|
||||
accessorKey: "max_streams",
|
||||
header: 'Max Streams',
|
||||
accessorKey: 'max_streams',
|
||||
size: 200,
|
||||
},
|
||||
{
|
||||
header: "Active",
|
||||
accessorKey: "is_active",
|
||||
header: 'Active',
|
||||
accessorKey: 'is_active',
|
||||
size: 100,
|
||||
sortingFn: "basic",
|
||||
sortingFn: 'basic',
|
||||
muiTableBodyCellProps: {
|
||||
align: "left",
|
||||
align: 'left',
|
||||
},
|
||||
Cell: ({ cell }) => (
|
||||
<Box sx={{ display: "flex", justifyContent: "center" }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
{cell.getValue() ? (
|
||||
<CheckIcon color="success" />
|
||||
) : (
|
||||
|
|
@ -92,11 +92,11 @@ const Example = () => {
|
|||
),
|
||||
filterFn: (row, _columnId, activeFilterValue) => {
|
||||
if (!activeFilterValue) return true; // Show all if no filter
|
||||
return String(row.getValue("is_active")) === activeFilterValue;
|
||||
return String(row.getValue('is_active')) === activeFilterValue;
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
//optionally access the underlying virtualizer instance
|
||||
|
|
@ -126,7 +126,7 @@ const Example = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
|
@ -157,7 +157,7 @@ const Example = () => {
|
|||
rowVirtualizerInstanceRef, //optional
|
||||
rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
|
||||
initialState: {
|
||||
density: "compact",
|
||||
density: 'compact',
|
||||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
|
|
@ -168,6 +168,7 @@ const Example = () => {
|
|||
onClick={() => {
|
||||
editPlaylist(row.original);
|
||||
}}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -175,6 +176,7 @@ const Example = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deletePlaylist(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -183,6 +185,7 @@ const Example = () => {
|
|||
color="info" // Red color for delete actions
|
||||
variant="contained"
|
||||
onClick={() => refreshPlaylist(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<RefreshIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -190,14 +193,14 @@ const Example = () => {
|
|||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: "calc(42vh - 0px)",
|
||||
height: 'calc(43vh - 0px)',
|
||||
},
|
||||
},
|
||||
renderTopToolbarCustomActions: ({ table }) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>M3U Accounts</Typography>
|
||||
|
|
@ -218,7 +221,7 @@ const Example = () => {
|
|||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: 2,
|
||||
padding: 1,
|
||||
}}
|
||||
>
|
||||
<MaterialReactTable table={table} />
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
MaterialReactTable,
|
||||
MRT_ShowHideColumnsButton,
|
||||
MRT_ToggleFullScreenButton,
|
||||
useMaterialReactTable,
|
||||
} from "material-react-table";
|
||||
} from 'material-react-table';
|
||||
import {
|
||||
Box,
|
||||
Grid2,
|
||||
|
|
@ -15,8 +15,8 @@ import {
|
|||
Checkbox,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@mui/material";
|
||||
import API from "../../api";
|
||||
} from '@mui/material';
|
||||
import API from '../../api';
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
Edit as EditIcon,
|
||||
|
|
@ -25,19 +25,19 @@ import {
|
|||
Check as CheckIcon,
|
||||
Close as CloseIcon,
|
||||
Refresh as RefreshIcon,
|
||||
} from "@mui/icons-material";
|
||||
import useEPGsStore from "../../store/epgs";
|
||||
import StreamProfileForm from "../forms/StreamProfile";
|
||||
import useStreamProfilesStore from "../../store/streamProfiles";
|
||||
import { TableHelper } from "../../helpers";
|
||||
} from '@mui/icons-material';
|
||||
import useEPGsStore from '../../store/epgs';
|
||||
import StreamProfileForm from '../forms/StreamProfile';
|
||||
import useStreamProfilesStore from '../../store/streamProfiles';
|
||||
import { TableHelper } from '../../helpers';
|
||||
|
||||
const StreamProfiles = () => {
|
||||
const [profile, setProfile] = useState(null);
|
||||
const [profileModalOpen, setProfileModalOpen] = useState(false);
|
||||
const [rowSelection, setRowSelection] = useState([]);
|
||||
const [snackbarMessage, setSnackbarMessage] = useState("");
|
||||
const [snackbarMessage, setSnackbarMessage] = useState('');
|
||||
const [snackbarOpen, setSnackbarOpen] = useState(false);
|
||||
const [activeFilterValue, setActiveFilterValue] = useState("all");
|
||||
const [activeFilterValue, setActiveFilterValue] = useState('all');
|
||||
|
||||
const streamProfiles = useStreamProfilesStore((state) => state.profiles);
|
||||
|
||||
|
|
@ -45,27 +45,27 @@ const StreamProfiles = () => {
|
|||
//column definitions...
|
||||
() => [
|
||||
{
|
||||
header: "Name",
|
||||
accessorKey: "profile_name",
|
||||
header: 'Name',
|
||||
accessorKey: 'profile_name',
|
||||
},
|
||||
{
|
||||
header: "Command",
|
||||
accessorKey: "command",
|
||||
header: 'Command',
|
||||
accessorKey: 'command',
|
||||
},
|
||||
{
|
||||
header: "Parameters",
|
||||
accessorKey: "parameters",
|
||||
header: 'Parameters',
|
||||
accessorKey: 'parameters',
|
||||
},
|
||||
{
|
||||
header: "Active",
|
||||
accessorKey: "is_active",
|
||||
header: 'Active',
|
||||
accessorKey: 'is_active',
|
||||
size: 100,
|
||||
sortingFn: "basic",
|
||||
sortingFn: 'basic',
|
||||
muiTableBodyCellProps: {
|
||||
align: "left",
|
||||
align: 'left',
|
||||
},
|
||||
Cell: ({ cell }) => (
|
||||
<Box sx={{ display: "flex", justifyContent: "center" }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
{cell.getValue() ? (
|
||||
<CheckIcon color="success" />
|
||||
) : (
|
||||
|
|
@ -93,12 +93,12 @@ const StreamProfiles = () => {
|
|||
</Box>
|
||||
),
|
||||
filterFn: (row, _columnId, filterValue) => {
|
||||
if (filterValue == "all") return true; // Show all if no filter
|
||||
return String(row.getValue("is_active")) === filterValue;
|
||||
if (filterValue == 'all') return true; // Show all if no filter
|
||||
return String(row.getValue('is_active')) === filterValue;
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
//optionally access the underlying virtualizer instance
|
||||
|
|
@ -117,7 +117,7 @@ const StreamProfiles = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
|
@ -148,7 +148,7 @@ const StreamProfiles = () => {
|
|||
rowVirtualizerInstanceRef, //optional
|
||||
rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
|
||||
initialState: {
|
||||
density: "compact",
|
||||
density: 'compact',
|
||||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
|
|
@ -157,6 +157,7 @@ const StreamProfiles = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="warning" // Red color for delete actions
|
||||
onClick={() => editStreamProfile(row.original)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -164,6 +165,7 @@ const StreamProfiles = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deleteStreamProfile(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -171,15 +173,15 @@ const StreamProfiles = () => {
|
|||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: "calc(100vh - 100px)", // Subtract padding to avoid cutoff
|
||||
overflowY: "auto", // Internal scrolling for the table
|
||||
height: 'calc(100vh - 73px)', // Subtract padding to avoid cutoff
|
||||
overflowY: 'auto', // Internal scrolling for the table
|
||||
},
|
||||
},
|
||||
renderTopToolbarCustomActions: ({ table }) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>Stream Profiles</Typography>
|
||||
|
|
@ -200,7 +202,7 @@ const StreamProfiles = () => {
|
|||
return (
|
||||
<Box
|
||||
sx={{
|
||||
padding: 2,
|
||||
padding: 1,
|
||||
}}
|
||||
>
|
||||
<MaterialReactTable table={table} />
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ const Example = () => {
|
|||
|
||||
columns,
|
||||
data: streams,
|
||||
// enableGlobalFilterModes: true,
|
||||
enablePagination: false,
|
||||
enableRowVirtualization: true,
|
||||
enableRowSelection: true,
|
||||
|
|
@ -132,39 +131,36 @@ const Example = () => {
|
|||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
<>
|
||||
<Tooltip
|
||||
title={
|
||||
row.original.m3u_account ? 'M3U streams locked' : 'Edit Stream'
|
||||
}
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="warning" // Red color for delete actions
|
||||
onClick={() => editStream(row.original)}
|
||||
disabled={row.original.m3u_account}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="warning" // Red color for delete actions
|
||||
onClick={() => editStream(row.original)}
|
||||
disabled={row.original.m3u_account}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<EditIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deleteStream(row.original.id)}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
<DeleteIcon fontSize="small" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
size="small" // Makes the button smaller
|
||||
color="success" // Red color for delete actions
|
||||
onClick={() => createChannelFromStream(row.original)}
|
||||
sx={{ p: 0 }}
|
||||
>
|
||||
<AddIcon fontSize="small" /> {/* Small icon size */}
|
||||
<AddIcon fontSize="small" />
|
||||
</IconButton>
|
||||
</>
|
||||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: 'calc(100vh - 90px)', // Subtract padding to avoid cutoff
|
||||
height: 'calc(100vh - 75px)', // Subtract padding to avoid cutoff
|
||||
overflowY: 'auto', // Internal scrolling for the table
|
||||
},
|
||||
},
|
||||
|
|
@ -199,6 +195,7 @@ const Example = () => {
|
|||
<Button
|
||||
variant="contained"
|
||||
onClick={createChannelsFromStreams}
|
||||
size="small"
|
||||
// disabled={rowSelection.length === 0}
|
||||
sx={{
|
||||
marginLeft: 1,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
MaterialReactTable,
|
||||
MRT_ShowHideColumnsButton,
|
||||
MRT_ToggleFullScreenButton,
|
||||
useMaterialReactTable,
|
||||
} from "material-react-table";
|
||||
} from 'material-react-table';
|
||||
import {
|
||||
Box,
|
||||
Grid2,
|
||||
|
|
@ -14,24 +14,24 @@ import {
|
|||
Tooltip,
|
||||
Select,
|
||||
MenuItem,
|
||||
} from "@mui/material";
|
||||
import API from "../../api";
|
||||
} from '@mui/material';
|
||||
import API from '../../api';
|
||||
import {
|
||||
Delete as DeleteIcon,
|
||||
Edit as EditIcon,
|
||||
Add as AddIcon,
|
||||
Check as CheckIcon,
|
||||
Close as CloseIcon,
|
||||
} from "@mui/icons-material";
|
||||
import useUserAgentsStore from "../../store/userAgents";
|
||||
import UserAgentForm from "../forms/UserAgent";
|
||||
import { TableHelper } from "../../helpers";
|
||||
} from '@mui/icons-material';
|
||||
import useUserAgentsStore from '../../store/userAgents';
|
||||
import UserAgentForm from '../forms/UserAgent';
|
||||
import { TableHelper } from '../../helpers';
|
||||
|
||||
const UserAgentsTable = () => {
|
||||
const [userAgent, setUserAgent] = useState(null);
|
||||
const [userAgentModalOpen, setUserAgentModalOpen] = useState(false);
|
||||
const [rowSelection, setRowSelection] = useState([]);
|
||||
const [activeFilterValue, setActiveFilterValue] = useState("all");
|
||||
const [activeFilterValue, setActiveFilterValue] = useState('all');
|
||||
|
||||
const userAgents = useUserAgentsStore((state) => state.userAgents);
|
||||
|
||||
|
|
@ -39,29 +39,27 @@ const UserAgentsTable = () => {
|
|||
//column definitions...
|
||||
() => [
|
||||
{
|
||||
header: "Name",
|
||||
size: 10,
|
||||
accessorKey: "user_agent_name",
|
||||
header: 'Name',
|
||||
accessorKey: 'user_agent_name',
|
||||
},
|
||||
{
|
||||
header: "User-Agent",
|
||||
accessorKey: "user_agent",
|
||||
size: 50,
|
||||
header: 'User-Agent',
|
||||
accessorKey: 'user_agent',
|
||||
},
|
||||
{
|
||||
header: "Desecription",
|
||||
accessorKey: "description",
|
||||
header: 'Desecription',
|
||||
accessorKey: 'description',
|
||||
},
|
||||
{
|
||||
header: "Active",
|
||||
accessorKey: "is_active",
|
||||
header: 'Active',
|
||||
accessorKey: 'is_active',
|
||||
size: 100,
|
||||
sortingFn: "basic",
|
||||
sortingFn: 'basic',
|
||||
muiTableBodyCellProps: {
|
||||
align: "left",
|
||||
align: 'left',
|
||||
},
|
||||
Cell: ({ cell }) => (
|
||||
<Box sx={{ display: "flex", justifyContent: "center" }}>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center' }}>
|
||||
{cell.getValue() ? (
|
||||
<CheckIcon color="success" />
|
||||
) : (
|
||||
|
|
@ -89,12 +87,12 @@ const UserAgentsTable = () => {
|
|||
</Box>
|
||||
),
|
||||
filterFn: (row, _columnId, activeFilterValue) => {
|
||||
if (activeFilterValue == "all") return true; // Show all if no filter
|
||||
return String(row.getValue("is_active")) === activeFilterValue;
|
||||
if (activeFilterValue == 'all') return true; // Show all if no filter
|
||||
return String(row.getValue('is_active')) === activeFilterValue;
|
||||
},
|
||||
},
|
||||
],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
//optionally access the underlying virtualizer instance
|
||||
|
|
@ -117,7 +115,7 @@ const UserAgentsTable = () => {
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
if (typeof window !== 'undefined') {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
|
@ -148,7 +146,7 @@ const UserAgentsTable = () => {
|
|||
rowVirtualizerInstanceRef, //optional
|
||||
rowVirtualizerOptions: { overscan: 5 }, //optionally customize the row virtualizer
|
||||
initialState: {
|
||||
density: "compact",
|
||||
density: 'compact',
|
||||
},
|
||||
enableRowActions: true,
|
||||
renderRowActions: ({ row }) => (
|
||||
|
|
@ -159,6 +157,7 @@ const UserAgentsTable = () => {
|
|||
onClick={() => {
|
||||
editUserAgent(row.original);
|
||||
}}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<EditIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -166,6 +165,7 @@ const UserAgentsTable = () => {
|
|||
size="small" // Makes the button smaller
|
||||
color="error" // Red color for delete actions
|
||||
onClick={() => deleteUserAgent(row.original.id)}
|
||||
sx={{ pt: 0, pb: 0 }}
|
||||
>
|
||||
<DeleteIcon fontSize="small" /> {/* Small icon size */}
|
||||
</IconButton>
|
||||
|
|
@ -173,14 +173,14 @@ const UserAgentsTable = () => {
|
|||
),
|
||||
muiTableContainerProps: {
|
||||
sx: {
|
||||
height: "calc(42vh - 10px)",
|
||||
height: 'calc(42vh + 5px)',
|
||||
},
|
||||
},
|
||||
renderTopToolbarCustomActions: ({ table }) => (
|
||||
<Stack
|
||||
direction="row"
|
||||
sx={{
|
||||
alignItems: "center",
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography>User-Agents</Typography>
|
||||
|
|
@ -202,7 +202,7 @@ const UserAgentsTable = () => {
|
|||
<>
|
||||
<Box
|
||||
sx={{
|
||||
padding: 2,
|
||||
padding: 1,
|
||||
}}
|
||||
>
|
||||
<MaterialReactTable table={table} />
|
||||
|
|
|
|||
|
|
@ -1,14 +1,33 @@
|
|||
export default {
|
||||
defaultProperties: {
|
||||
enableGlobalFilter: false,
|
||||
enableBottomToolbar: false,
|
||||
enableDensityToggle: false,
|
||||
enableFullScreenToggle: false,
|
||||
positionToolbarAlertBanner: "none",
|
||||
columnFilterDisplayMode: "popover",
|
||||
positionToolbarAlertBanner: 'none',
|
||||
columnFilterDisplayMode: 'popover',
|
||||
enableRowNumbers: false,
|
||||
positionActionsColumn: "last",
|
||||
positionActionsColumn: 'last',
|
||||
initialState: {
|
||||
density: "compact",
|
||||
density: 'compact',
|
||||
},
|
||||
muiTableBodyCellProps: {
|
||||
sx: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
muiTableHeadCellProps: {
|
||||
sx: {
|
||||
padding: 0,
|
||||
},
|
||||
},
|
||||
muiTableBodyProps: {
|
||||
sx: {
|
||||
//stripe the rows, make odd rows a darker color
|
||||
'& tr:nth-of-type(odd) > td': {
|
||||
backgroundColor: '#f5f5f5',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
|
@ -1,7 +1,7 @@
|
|||
import React from "react";
|
||||
import ChannelsTable from "../components/tables/ChannelsTable";
|
||||
import StreamsTable from "../components/tables/StreamsTable";
|
||||
import { Grid2, Box } from "@mui/material";
|
||||
import React from 'react';
|
||||
import ChannelsTable from '../components/tables/ChannelsTable';
|
||||
import StreamsTable from '../components/tables/StreamsTable';
|
||||
import { Grid2, Box } from '@mui/material';
|
||||
|
||||
const ChannelsPage = () => {
|
||||
return (
|
||||
|
|
@ -9,13 +9,13 @@ const ChannelsPage = () => {
|
|||
<Grid2 size={6}>
|
||||
<Box
|
||||
sx={{
|
||||
height: "100vh", // Full viewport height
|
||||
paddingTop: "20px", // Top padding
|
||||
paddingBottom: "20px", // Bottom padding
|
||||
paddingRight: "10px",
|
||||
paddingLeft: "20px",
|
||||
boxSizing: "border-box", // Include padding in height calculation
|
||||
overflow: "hidden", // Prevent parent scrolling
|
||||
height: '100vh', // Full viewport height
|
||||
paddingTop: 1, // Top padding
|
||||
paddingBottom: 1, // Bottom padding
|
||||
paddingRight: 0.5,
|
||||
paddingLeft: 1,
|
||||
boxSizing: 'border-box', // Include padding in height calculation
|
||||
overflow: 'hidden', // Prevent parent scrolling
|
||||
}}
|
||||
>
|
||||
<ChannelsTable />
|
||||
|
|
@ -24,13 +24,13 @@ const ChannelsPage = () => {
|
|||
<Grid2 size={6}>
|
||||
<Box
|
||||
sx={{
|
||||
height: "100vh", // Full viewport height
|
||||
paddingTop: "20px", // Top padding
|
||||
paddingBottom: "20px", // Bottom padding
|
||||
paddingRight: "20px",
|
||||
paddingLeft: "10px",
|
||||
boxSizing: "border-box", // Include padding in height calculation
|
||||
overflow: "hidden", // Prevent parent scrolling
|
||||
height: '100vh', // Full viewport height
|
||||
paddingTop: 1, // Top padding
|
||||
paddingBottom: 1, // Bottom padding
|
||||
paddingRight: 1,
|
||||
paddingLeft: 0.5,
|
||||
boxSizing: 'border-box', // Include padding in height calculation
|
||||
overflow: 'hidden', // Prevent parent scrolling
|
||||
}}
|
||||
>
|
||||
<StreamsTable />
|
||||
|
|
|
|||
|
|
@ -1,91 +1,325 @@
|
|||
import React from 'react';
|
||||
import { useEpg, Epg, Layout } from 'planby';
|
||||
import React, { useMemo, useState, useEffect, useRef } from 'react';
|
||||
import { Box, Typography, Avatar, Paper, Tooltip, Stack } from '@mui/material';
|
||||
import dayjs from 'dayjs';
|
||||
import API from '../api';
|
||||
import useChannelsStore from '../store/channels';
|
||||
import logo from '../images/logo.png';
|
||||
|
||||
function App() {
|
||||
const [channels, setChannels] = React.useState([]);
|
||||
const [epg, setEpg] = React.useState([]);
|
||||
const CHANNEL_WIDTH = 100;
|
||||
const PROGRAM_HEIGHT = 80;
|
||||
const HOUR_WIDTH = 300;
|
||||
|
||||
const fetchChannels = async () => {
|
||||
const channels = await API.getChannels();
|
||||
const retval = [];
|
||||
for (const channel of channels) {
|
||||
if (!channel.tvg_id) {
|
||||
continue;
|
||||
}
|
||||
console.log(channel);
|
||||
retval.push({
|
||||
uuid: channel.tvg_id,
|
||||
type: 'channel',
|
||||
title: channel.channel_name,
|
||||
country: 'USA',
|
||||
provider: channel.channel_group?.name || 'Default',
|
||||
logo: channel.logo_url || '/images/logo.png',
|
||||
year: 2025,
|
||||
});
|
||||
}
|
||||
const TVChannelGuide = ({ startDate, endDate }) => {
|
||||
const { channels } = useChannelsStore();
|
||||
|
||||
setChannels(retval);
|
||||
return retval;
|
||||
};
|
||||
const [programs, setPrograms] = useState([]);
|
||||
const [guideChannels, setGuideChannels] = useState([]);
|
||||
const [now, setNow] = useState(dayjs());
|
||||
|
||||
const fetchEpg = async () => {
|
||||
const programs = await API.getGrid();
|
||||
const retval = [];
|
||||
console.log(programs);
|
||||
for (const program of programs.data) {
|
||||
retval.push({
|
||||
id: program.id,
|
||||
channelUuid: 'Nickelodeon (East).us',
|
||||
description: program.description,
|
||||
title: program.title,
|
||||
since: program.start_time,
|
||||
till: program.end_time,
|
||||
});
|
||||
}
|
||||
const guideRef = useRef(null);
|
||||
|
||||
setEpg(retval);
|
||||
return retval;
|
||||
};
|
||||
|
||||
const fetchData = async () => {
|
||||
const channels = await fetchChannels();
|
||||
const epg = await fetchEpg();
|
||||
|
||||
setChannels(channels);
|
||||
setEpg(epg);
|
||||
};
|
||||
|
||||
if (channels.length === 0) {
|
||||
fetchData();
|
||||
if (!channels || channels.length === 0) {
|
||||
console.warn('No channels provided or empty channels array');
|
||||
}
|
||||
|
||||
const formatDate = (date) => date.toISOString().split('T')[0] + 'T00:00:00';
|
||||
useEffect(() => {
|
||||
const fetchPrograms = async () => {
|
||||
const programs = await API.getGrid();
|
||||
const programIds = [...new Set(programs.map((prog) => prog.tvg_id))];
|
||||
console.log(programIds);
|
||||
const filteredChannels = channels.filter((ch) =>
|
||||
programIds.includes(ch.tvg_id)
|
||||
);
|
||||
console.log(filteredChannels);
|
||||
setGuideChannels(filteredChannels);
|
||||
setPrograms(programs);
|
||||
};
|
||||
|
||||
const today = new Date();
|
||||
const tomorrow = new Date(today);
|
||||
fetchPrograms();
|
||||
}, [channels]);
|
||||
|
||||
const {
|
||||
getEpgProps,
|
||||
getLayoutProps,
|
||||
onScrollToNow,
|
||||
onScrollLeft,
|
||||
onScrollRight,
|
||||
} = useEpg({
|
||||
epg,
|
||||
channels,
|
||||
startDate: '2025-02-25T11:00:00', // or 2022-02-02T00:00:00
|
||||
width: '100%',
|
||||
height: 600,
|
||||
});
|
||||
const latestHalfHour = new Date();
|
||||
|
||||
// Round down the minutes to the nearest half hour
|
||||
const minutes = latestHalfHour.getMinutes();
|
||||
const roundedMinutes = minutes < 30 ? 0 : 30;
|
||||
|
||||
latestHalfHour.setMinutes(roundedMinutes);
|
||||
latestHalfHour.setSeconds(0);
|
||||
latestHalfHour.setMilliseconds(0);
|
||||
|
||||
const todayMidnight = dayjs().startOf('day');
|
||||
|
||||
const start = dayjs(startDate || todayMidnight);
|
||||
const end = endDate ? dayjs(endDate) : start.add(24, 'hour');
|
||||
|
||||
const timeline = useMemo(() => {
|
||||
// console.log('Generating timeline...');
|
||||
const hours = [];
|
||||
let current = start;
|
||||
while (current.isBefore(end)) {
|
||||
hours.push(current);
|
||||
current = current.add(1, 'hour');
|
||||
}
|
||||
// console.log('Timeline generated:', hours);
|
||||
return hours;
|
||||
}, [start, end]);
|
||||
|
||||
useEffect(() => {
|
||||
if (guideRef.current) {
|
||||
const nowOffset = dayjs().diff(start, 'minute');
|
||||
const scrollPosition = (nowOffset / 60) * HOUR_WIDTH - HOUR_WIDTH;
|
||||
guideRef.current.scrollLeft = Math.max(scrollPosition, 0);
|
||||
}
|
||||
}, [programs, start]);
|
||||
|
||||
const renderProgram = (program, channelStart) => {
|
||||
const programStart = dayjs(program.start_time);
|
||||
const programEnd = dayjs(program.end_time);
|
||||
const startOffset = programStart.diff(channelStart, 'minute');
|
||||
const duration = programEnd.diff(programStart, 'minute');
|
||||
|
||||
const now = dayjs();
|
||||
const isLive =
|
||||
dayjs(program.start_time).isBefore(now) &&
|
||||
dayjs(program.end_time).isAfter(now);
|
||||
|
||||
return (
|
||||
// <Tooltip title={`${program.title} - ${program.description}`} arrow>
|
||||
<Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: (startOffset / 60) * HOUR_WIDTH + 2,
|
||||
width: (duration / 60) * HOUR_WIDTH - 4,
|
||||
top: 2,
|
||||
height: PROGRAM_HEIGHT - 4,
|
||||
padding: 1,
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
// borderLeft: '1px solid black',
|
||||
borderRight: '1px solid black',
|
||||
borderRadius: '8px',
|
||||
color: 'primary.contrastText',
|
||||
background: isLive
|
||||
? 'linear-gradient(to right, #1a202c, #1a202c, #002eb3)'
|
||||
: 'linear-gradient(to right, #1a202c, #1a202c)',
|
||||
'&:hover': {
|
||||
background: 'linear-gradient(to right, #051937, #002360)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
variant="body2"
|
||||
noWrap
|
||||
sx={{
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
{program.title}
|
||||
</Typography>
|
||||
<Typography variant="overline" noWrap>
|
||||
{programStart.format('h:mma')} - {programEnd.format('h:mma')}
|
||||
</Typography>
|
||||
</Box>
|
||||
// </Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setNow(dayjs());
|
||||
}, 60000); // Update every minute
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
const nowPosition = useMemo(() => {
|
||||
if (now.isBefore(start) || now.isAfter(end)) return -1;
|
||||
const totalMinutes = end.diff(start, 'minute');
|
||||
const minutesSinceStart = now.diff(start, 'minute');
|
||||
return (minutesSinceStart / totalMinutes) * (timeline.length * HOUR_WIDTH);
|
||||
}, [now, start, end, timeline.length]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Epg {...getEpgProps()}>
|
||||
<Layout {...getLayoutProps()} />
|
||||
</Epg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
<Box
|
||||
sx={{
|
||||
overflow: 'hidden',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: '#171923',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6">
|
||||
Channels length: {guideChannels?.length ?? 0}
|
||||
</Typography>
|
||||
|
||||
export default App;
|
||||
<Stack direction="row">
|
||||
<Box>
|
||||
{/* Channel Column */}
|
||||
<Box
|
||||
sx={{
|
||||
width: CHANNEL_WIDTH,
|
||||
height: '40px',
|
||||
}}
|
||||
/>
|
||||
{guideChannels.map((channel, index) => {
|
||||
return (
|
||||
<Box
|
||||
key={index}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
// borderTop: '1px solid #ccc',
|
||||
height: PROGRAM_HEIGHT + 1,
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: CHANNEL_WIDTH,
|
||||
display: 'flex',
|
||||
padding: 1,
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
src={channel.logo_url || logo}
|
||||
alt={channel.channel_name}
|
||||
/>
|
||||
{/* <Typography variant="body2" sx={{ marginLeft: 1 }}>
|
||||
{channel.channel_name}
|
||||
</Typography> */}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
|
||||
{/* Timeline and Lineup */}
|
||||
<Box ref={guideRef} sx={{ overflowY: 'auto', height: '100%' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
position: 'sticky',
|
||||
top: 0,
|
||||
zIndex: 10,
|
||||
backgroundColor: '#fff',
|
||||
}}
|
||||
>
|
||||
<Box sx={{ flex: 1, display: 'flex' }}>
|
||||
{timeline.map((time, index) => (
|
||||
<Box
|
||||
key={time.format()}
|
||||
sx={{
|
||||
width: HOUR_WIDTH,
|
||||
// borderLeft: '1px solid #ddd',
|
||||
padding: 1,
|
||||
backgroundColor: '#171923',
|
||||
color: 'primary.contrastText',
|
||||
height: '40px',
|
||||
alignItems: 'center',
|
||||
position: 'relative',
|
||||
padding: 0,
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="body2"
|
||||
sx={{
|
||||
color: '#a0aec0',
|
||||
position: 'absolute',
|
||||
left: index == 0 ? 0 : '-18px',
|
||||
}}
|
||||
>
|
||||
{time.format('h:mma')}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
display: 'grid',
|
||||
alignItems: 'end',
|
||||
'grid-template-columns': 'repeat(4, 1fr)',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: '1px',
|
||||
height: '10px',
|
||||
marginRight: HOUR_WIDTH / 4 + 'px',
|
||||
background: '#718096',
|
||||
}}
|
||||
></Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '1px',
|
||||
height: '10px',
|
||||
marginRight: HOUR_WIDTH / 4 + 'px',
|
||||
background: '#718096',
|
||||
}}
|
||||
></Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '1px',
|
||||
height: '10px',
|
||||
marginRight: HOUR_WIDTH / 4 + 'px',
|
||||
background: '#718096',
|
||||
}}
|
||||
></Box>
|
||||
<Box
|
||||
sx={{
|
||||
width: '1px',
|
||||
height: '10px',
|
||||
marginRight: HOUR_WIDTH / 4 + 'px',
|
||||
background: '#718096',
|
||||
}}
|
||||
></Box>
|
||||
</Box>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
{nowPosition > 0 && (
|
||||
<Box
|
||||
className="now-position"
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: nowPosition,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
width: '3px',
|
||||
backgroundColor: 'rgb(44, 122, 123)',
|
||||
zIndex: 15,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{guideChannels.map((channel, index) => {
|
||||
const channelPrograms = programs.filter(
|
||||
(p) => p.tvg_id === channel.tvg_id
|
||||
);
|
||||
return (
|
||||
<Box key={index} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
minHeight: PROGRAM_HEIGHT,
|
||||
}}
|
||||
>
|
||||
{channelPrograms.map((program) =>
|
||||
renderProgram(program, start)
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default TVChannelGuide;
|
||||
|
|
|
|||
|
|
@ -1,38 +1,36 @@
|
|||
export default {
|
||||
Limiter: (concurrency, promiseList) => {
|
||||
if (!promiseList || promiseList.length === 0) {
|
||||
return Promise.resolve([]); // Return a resolved empty array if no promises
|
||||
Limiter: (n, list) => {
|
||||
if (!list || !list.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let index = 0; // Keeps track of the current promise to be processed
|
||||
const results = []; // Stores the results of all promises
|
||||
const totalPromises = promiseList.length;
|
||||
var tail = list.splice(n);
|
||||
var head = list;
|
||||
var resolved = [];
|
||||
var processed = 0;
|
||||
|
||||
// Helper function to process promises one by one, respecting concurrency
|
||||
const processNext = () => {
|
||||
// If we've processed all promises, resolve with the results
|
||||
if (index >= totalPromises) {
|
||||
return Promise.all(results);
|
||||
}
|
||||
|
||||
// Execute the current promise and store the result
|
||||
const currentPromise = promiseList[index]();
|
||||
results.push(currentPromise);
|
||||
|
||||
// Once the current promise resolves, move on to the next one
|
||||
return currentPromise.then(() => {
|
||||
index++; // Move to the next promise
|
||||
return processNext(); // Process the next promise
|
||||
return new Promise(function (resolve) {
|
||||
head.forEach(function (x) {
|
||||
var res = x();
|
||||
resolved.push(res);
|
||||
res.then(function (y) {
|
||||
runNext();
|
||||
return y;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Start processing promises up to the given concurrency
|
||||
const concurrencyPromises = [];
|
||||
for (let i = 0; i < concurrency && i < totalPromises; i++) {
|
||||
concurrencyPromises.push(processNext());
|
||||
}
|
||||
|
||||
// Wait for all promises to resolve
|
||||
return Promise.all(concurrencyPromises).then(() => results);
|
||||
}
|
||||
}
|
||||
function runNext() {
|
||||
if (processed == tail.length) {
|
||||
resolve(Promise.all(resolved));
|
||||
} else {
|
||||
resolved.push(
|
||||
tail[processed]().then(function (x) {
|
||||
runNext();
|
||||
return x;
|
||||
})
|
||||
);
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
# https://github.com/browserslist/browserslist#readme
|
||||
|
||||
>= 0.5%
|
||||
last 2 major versions
|
||||
not dead
|
||||
Chrome >= 60
|
||||
Firefox >= 60
|
||||
Firefox ESR
|
||||
iOS >= 12
|
||||
Safari >= 12
|
||||
not Explorer <= 11
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
{
|
||||
"files": [
|
||||
{
|
||||
"path": "./dist/css/adminlte.css",
|
||||
"maxSize": "43 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/adminlte.min.css",
|
||||
"maxSize": "40.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/adminlte.rtl.css",
|
||||
"maxSize": "43 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/css/adminlte.rtl.min.css",
|
||||
"maxSize": "40.25 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/adminlte.js",
|
||||
"maxSize": "4.5 kB"
|
||||
},
|
||||
{
|
||||
"path": "./dist/js/adminlte.min.js",
|
||||
"maxSize": "3 kB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
**/*.min.js
|
||||
**/plugins/
|
||||
/.temp/
|
||||
/dist/
|
||||
/docs/
|
||||
/docs_html/
|
||||
**/env.d.ts
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": [
|
||||
"plugin:import/errors",
|
||||
"plugin:import/warnings",
|
||||
"plugin:unicorn/recommended",
|
||||
"xo",
|
||||
"xo/browser"
|
||||
],
|
||||
"rules": {
|
||||
"arrow-body-style": "off",
|
||||
"capitalized-comments": "off",
|
||||
"comma-dangle": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"MemberExpression": "off",
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"max-params": [
|
||||
"warn",
|
||||
5
|
||||
],
|
||||
"multiline-ternary": [
|
||||
"error",
|
||||
"always-multiline"
|
||||
],
|
||||
"new-cap": [
|
||||
"error",
|
||||
{
|
||||
"properties": false
|
||||
}
|
||||
],
|
||||
"no-console": "error",
|
||||
"no-negated-condition": "off",
|
||||
"object-curly-spacing": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
"error",
|
||||
"after"
|
||||
],
|
||||
"semi": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"unicorn/explicit-length-check": "off",
|
||||
"unicorn/no-array-callback-reference": "off",
|
||||
"unicorn/no-array-for-each": "off",
|
||||
"unicorn/no-array-method-this-argument": "off",
|
||||
"unicorn/no-null": "off",
|
||||
"unicorn/no-unused-properties": "error",
|
||||
"unicorn/prefer-array-flat": "off",
|
||||
"unicorn/prefer-dom-node-dataset": "off",
|
||||
"unicorn/prefer-export-from": "off",
|
||||
"unicorn/prefer-module": "off",
|
||||
"unicorn/prefer-query-selector": "off",
|
||||
"unicorn/prefer-spread": "off",
|
||||
"unicorn/prefer-string-replace-all": "off",
|
||||
"unicorn/prevent-abbreviations": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": [
|
||||
"plugin:import/typescript",
|
||||
"xo-typescript"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/comma-dangle": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"@typescript-eslint/indent": [
|
||||
"error",
|
||||
2,
|
||||
{
|
||||
"MemberExpression": "off",
|
||||
"SwitchCase": 1
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "variable",
|
||||
"format": ["camelCase", "StrictPascalCase", "UPPER_CASE"]
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/object-curly-spacing": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"never"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/config/**"],
|
||||
"env": {
|
||||
"browser": false,
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
"no-console": "off",
|
||||
"unicorn/prefer-top-level-await": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
6
static/admin-lte/.gitattributes
vendored
|
|
@ -1,6 +0,0 @@
|
|||
# Enforce Unix newlines
|
||||
* text=auto eol=lf
|
||||
|
||||
# Ignores for analysis is used to produce the language stats bar which displays the languages percentages
|
||||
plugins/* linguist-vendored=true
|
||||
docs/* linguist-vendored=true
|
||||
34
static/admin-lte/.github/CONTRIBUTING.md
vendored
|
|
@ -1,34 +0,0 @@
|
|||
# Contributing to AdminLTE
|
||||
|
||||
Contributions are always **welcome and recommended**! Here is how for beginner's: [Get started with open source click here](https://youtu.be/GbqSvJs-6W4)
|
||||
|
||||
1. Contribution Requirements :
|
||||
* When you contribute, you agree to give a non-exclusive license to AdminLTE.io to use that contribution in any context as we (AdminLTE.io) see appropriate.
|
||||
* If you use content provided by another party, it must be appropriately licensed using an [open source](https://opensource.org/licenses) license.
|
||||
* Contributions are only accepted through GitHub pull requests.
|
||||
* Finally, contributed code must work in all supported browsers (see above for browser support).
|
||||
2. Installation :
|
||||
* Fork the repository ([here is the guide](https://help.github.com/articles/fork-a-repo/)).
|
||||
* Clone to your machine
|
||||
|
||||
```bash
|
||||
git clone https://github.com/YOUR_USERNAME/AdminLTE.git
|
||||
```
|
||||
* Create a new branch from `master`
|
||||
3. Compile dist files (Development) :
|
||||
* To compile the dist files you need Node.js 18 or higher/npm (node package manager)
|
||||
* `npm install` (install npm deps)
|
||||
* `npm run dev` (developer mode, autocompile with browsersync support for live demo)
|
||||
* Make your changes only in `./src` Folder OR `package.json` in any files which are necessary for contribution
|
||||
* Do not make changes in `./dist/**` Because it contains compiled files and do not include in PR (Pull Request)
|
||||
* `npm run production` (compile css/js files and test all pages are perfectly working fine, before creating a pull request)
|
||||
4. Create a pull request to `master` branch
|
||||
|
||||
## Online one-click setup for contributing
|
||||
|
||||
You can use [Codespace](https://docs.github.com/en/codespaces) an online IDE which is free for Open Source for working on issues or making PRs (Pull Requests). With a single click it will launch a workspace and automatically:
|
||||
|
||||
- clone the `AdminLTE` repo.
|
||||
- Open with [Codespace](https://docs.github.com/en/codespaces) or [](https://gitpod.io/from-referrer/)
|
||||
- install the dependencies.
|
||||
- run `npm run dev` to start the server.
|
||||
2
static/admin-lte/.github/FUNDING.yml
vendored
|
|
@ -1,2 +0,0 @@
|
|||
github: danny007in
|
||||
custom: ["https://www.paypal.me/daniel007in"]
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
---
|
||||
name: Bug report for AdminLTE v3.x
|
||||
about: Create a report to help us improve AdminLTE v3.x
|
||||
title: "[BUG]"
|
||||
labels: type:bug, version:3.1.x
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment (please complete the following information):**
|
||||
- AdminLTE Version: [e.g. v3.0.0]
|
||||
- Operating System: [e.g. macOS Catalina]
|
||||
- Browser (Version): [e.g. Chrome (Latest)]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request for AdminLTE v4.x
|
||||
about: Suggest an idea for this project
|
||||
title: "[FEATURE]"
|
||||
labels: type:enhancement, version:4.x
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
name: "CodeQL config"
|
||||
|
||||
paths-ignore:
|
||||
- dist
|
||||
35
static/admin-lte/.github/dependabot.yml
vendored
|
|
@ -1,35 +0,0 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: npm
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: monthly
|
||||
time: "03:00"
|
||||
groups:
|
||||
eslint:
|
||||
patterns:
|
||||
- "eslint"
|
||||
- "eslint-*"
|
||||
- "@typescript-eslint/*"
|
||||
stylelint:
|
||||
patterns:
|
||||
- "stylelint"
|
||||
- "stylelint-*"
|
||||
prettier:
|
||||
patterns:
|
||||
- "prettier"
|
||||
- "prettier-*"
|
||||
rollup:
|
||||
patterns:
|
||||
- "rollup"
|
||||
- "@rollup/*"
|
||||
postcss:
|
||||
patterns:
|
||||
- "postcss"
|
||||
- "postcss-*"
|
||||
astro:
|
||||
patterns:
|
||||
- "astro"
|
||||
- "@astrojs/*"
|
||||
open-pull-requests-limit: 10
|
||||
versioning-strategy: increase
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
name: Bundlewatch
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
NODE: 18
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
bundlewatch:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "${{ env.NODE }}"
|
||||
cache: npm
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run dist
|
||||
run: npm run compile
|
||||
|
||||
- name: Run bundlewatch
|
||||
run: npm run bundlewatch
|
||||
env:
|
||||
BUNDLEWATCH_GITHUB_TOKEN: "${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}"
|
||||
CI_BRANCH_BASE: master
|
||||
44
static/admin-lte/.github/workflows/codeql.yml
vendored
|
|
@ -1,44 +0,0 @@
|
|||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "!dependabot/**"
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches:
|
||||
- master
|
||||
- "!dependabot/**"
|
||||
schedule:
|
||||
- cron: "0 0 * * 0"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: "javascript"
|
||||
config-file: ./.github/codeql/codeql-config.yml
|
||||
queries: +security-and-quality
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:javascript"
|
||||
37
static/admin-lte/.github/workflows/docs.yml
vendored
|
|
@ -1,37 +0,0 @@
|
|||
name: Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
NODE: 18
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
bundlewatch:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "${{ env.NODE }}"
|
||||
cache: npm
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build docs
|
||||
run: npm run docs-compile
|
||||
37
static/admin-lte/.github/workflows/lint.yml
vendored
|
|
@ -1,37 +0,0 @@
|
|||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
NODE: 18
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "${{ env.NODE }}"
|
||||
cache: npm
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Run lint
|
||||
run: npm run lint
|
||||
54
static/admin-lte/.github/workflows/node-sass.yml
vendored
|
|
@ -1,54 +0,0 @@
|
|||
name: CSS (node-sass)
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- "dependabot/**"
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 2
|
||||
NODE: 18
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
css:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "${{ env.NODE }}"
|
||||
cache: npm
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build CSS with node-sass
|
||||
run: |
|
||||
npx --package node-sass@latest node-sass --version
|
||||
npx --package node-sass@latest node-sass --include-path=node_modules --output-style expanded --source-map true --source-map-contents true --precision 6 src/scss/ -o dist-sass/css/
|
||||
ls -Al dist-sass/css
|
||||
|
||||
# Check that there are no Sass variables (`$`)
|
||||
- name: Check built CSS files
|
||||
shell: bash
|
||||
run: |
|
||||
SASS_VARS_FOUND=$(find "dist-sass/css/" -type f -name "*.css" -print0 | xargs -0 --no-run-if-empty grep -F "\$" || true)
|
||||
if [[ -z "$SASS_VARS_FOUND" ]]; then
|
||||
echo "All good, no Sass variables found!"
|
||||
exit 0
|
||||
else
|
||||
echo "Found $(echo "$SASS_VARS_FOUND" | wc -l | bc) Sass variables:"
|
||||
echo "$SASS_VARS_FOUND"
|
||||
exit 1
|
||||
fi
|
||||
50
static/admin-lte/.github/workflows/release.yml
vendored
|
|
@ -1,50 +0,0 @@
|
|||
name: Release & Publish
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set env
|
||||
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "${{ env.NODE }}"
|
||||
cache: npm
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Build distribution files
|
||||
run: npm run production
|
||||
|
||||
- name: Zip distribution files
|
||||
uses: montudor/action-zip@v1
|
||||
with:
|
||||
args: "zip -qq admin-lte-${{env.RELEASE_VERSION}}.zip -d dist"
|
||||
|
||||
- name: Create changelog text
|
||||
id: changelog
|
||||
uses: endaft/action-changelog@v0.0.5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
exclude_types: other,doc,chore
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
body: ${{ steps.changelog.outputs.changelog }}
|
||||
files: |
|
||||
admin-lte.${{env.RELEASE_VERSION}}.zip
|
||||
40
static/admin-lte/.gitignore
vendored
|
|
@ -1,40 +0,0 @@
|
|||
# generated types
|
||||
.astro/
|
||||
|
||||
# unwanted types
|
||||
**/env.d.ts
|
||||
|
||||
# System / Log files
|
||||
*.DS_Store
|
||||
*.log
|
||||
|
||||
# Archives
|
||||
*.zip
|
||||
|
||||
# Sass Cache
|
||||
.sass-cache
|
||||
|
||||
# Project files
|
||||
.idea
|
||||
nbproject
|
||||
nbproject/private
|
||||
.vscode/
|
||||
.vs/
|
||||
|
||||
# Node / Bower
|
||||
node_modules/
|
||||
bower_components/
|
||||
|
||||
# Docs
|
||||
/docs/_site/
|
||||
/docs/vendor/
|
||||
/docs/.bundle/
|
||||
/docs_html/
|
||||
.jekyll-cache/
|
||||
.jekyll-metadata
|
||||
|
||||
# ETC
|
||||
TODO
|
||||
test.html
|
||||
ad.js
|
||||
/.cache/
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
tasks:
|
||||
- init: npm install
|
||||
command: npm run dev
|
||||
ports:
|
||||
- port: 3000
|
||||
onOpen: open-preview
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
# https://lgtm.com/help/lgtm/customizing-file-classification
|
||||
path_classifiers:
|
||||
plugins:
|
||||
- plugins/
|
||||
|
||||
extraction:
|
||||
javascript:
|
||||
# https://lgtm.com/help/lgtm/javascript-extraction#customizing-index
|
||||
# The `index` step extracts information from the files in the codebase.
|
||||
index:
|
||||
# Specify a list of files and folders to exclude from extraction.
|
||||
exclude:
|
||||
- bower_components/
|
||||
- docs/assets/js/plugins/
|
||||
- plugins/
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
/docs/
|
||||
/docs_html/
|
||||
/plugins/*
|
||||
!/plugins/flot-old/
|
||||
/.github/
|
||||
/.temp/
|
||||
/.lgtm.yml
|
||||
/.cache/
|
||||
/.idea/
|
||||
/.browserlistrc
|
||||
/.bundlewatch.config.json
|
||||
/.editorconfig
|
||||
/.eslintignore
|
||||
/.eslintrc.json
|
||||
/.gitattributes
|
||||
/.gitignore
|
||||
/.gitpod.yml
|
||||
/.stylelintignore
|
||||
/.stylelintrc.json
|
||||
/composer.json
|
||||
/tsconfig.json
|
||||
/src/assets/
|
||||
/src/config/
|
||||
/src/html/
|
||||
/src/utils/
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"tabWidth": 2
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
**/*.html
|
||||
**/*.md
|
||||
**/*.min.css
|
||||
**/.temp/
|
||||
**/dist/
|
||||
**/docs_html/
|
||||
**/plugins/
|
||||
**/.cache/
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
{
|
||||
"extends": [
|
||||
"stylelint-config-twbs-bootstrap"
|
||||
],
|
||||
"reportInvalidScopeDisables": true,
|
||||
"reportNeedlessDisables": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.scss"],
|
||||
"rules": {
|
||||
"declaration-no-important": null,
|
||||
"declaration-property-value-disallowed-list": {
|
||||
"border": "none",
|
||||
"outline": "none"
|
||||
},
|
||||
"function-disallowed-list": [
|
||||
"calc",
|
||||
"lighten",
|
||||
"darken"
|
||||
],
|
||||
"keyframes-name-pattern": null,
|
||||
"property-disallowed-list": [
|
||||
"border-radius",
|
||||
"border-top-left-radius",
|
||||
"border-top-right-radius",
|
||||
"border-bottom-right-radius",
|
||||
"border-bottom-left-radius",
|
||||
"transition"
|
||||
],
|
||||
"scss/dollar-variable-default": [
|
||||
true,
|
||||
{
|
||||
"ignore": "local"
|
||||
}
|
||||
],
|
||||
"scss/selector-no-union-class-name": true,
|
||||
"selector-max-class": null,
|
||||
"selector-max-combinators": null,
|
||||
"selector-max-compound-selectors": null,
|
||||
"selector-max-id": null,
|
||||
"selector-max-specificity": null,
|
||||
"selector-max-type": null,
|
||||
"selector-no-qualifying-type": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at https://colorlib.com/wp/contact-us/. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.4, available at <https://www.contributor-covenant.org/version/1/4/code-of-conduct/>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014-2023 ColorlibHQ
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
# [AdminLTE - Bootstrap 5 Admin Dashboard](https://adminlte.io)
|
||||
|
||||
[](https://www.npmjs.com/package/admin-lte)
|
||||
[](https://packagist.org/packages/almasaeed2010/adminlte)
|
||||
[](https://www.jsdelivr.com/package/npm/admin-lte)
|
||||
[](https://discord.gg/jfdvjwFqfz)
|
||||
[](https://app.netlify.com/sites/adminlte-v4/deploys)
|
||||
|
||||
**AdminLTE** is a fully responsive administration template. Based on **[Bootstrap 5](https://getbootstrap.com/)** framework and also the JavaScript plugins.
|
||||
Highly customizable and easy to use. Fits many screen resolutions from small mobile devices to large desktops.
|
||||
|
||||
## Looking for Premium Templates?
|
||||
|
||||
AdminLTE.io just opened a new premium templates page. Hand picked to ensure the best quality and the most affordable
|
||||
prices. Visit <https://adminlte.io/premium> for more information.
|
||||
|
||||

|
||||
|
||||
**AdminLTE** has been carefully coded with clear comments in all of its JS, SCSS and HTML files.
|
||||
SCSS has been used to increase code customizability.
|
||||
|
||||
## Quick start
|
||||
|
||||
### Compile dist files
|
||||
|
||||
To compile the dist files you need Node.js/npm, clone/download the repo then:
|
||||
|
||||
1. `npm install` (install npm deps)
|
||||
2. _Optional:_ `npm run dev` (developer mode, autocompile with browsersync support for live demo)
|
||||
3. `npm run production` (compile css/js files)
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
- Highly welcome.
|
||||
- For your extra reference check [AdminLTE v4 Contribution Guide](https://github.com/ColorlibHQ/AdminLTE#contributing)
|
||||
- First thing first, you should have bit knowledge about NodeJS.
|
||||
- Github Knowledge.
|
||||
- Install NodeJS LTS version.
|
||||
- Clone this Repository to your machine and change to `master` branch.
|
||||
- Go to Cloned Folder.
|
||||
- In cli/bash run `npm install` it will install dependency from `package.json`.
|
||||
- After installation completes, run `npm run dev`
|
||||
- Cool, Send urs changes in PR to `master` branch.
|
||||
|
||||
## Sponsorship
|
||||
|
||||
Support AdminLTE development by becoming a sponsor.
|
||||
[Github Sponsors](https://github.com/sponsors/danny007in) or
|
||||
[PayPal](https://www.paypal.me/daniel007in)
|
||||
|
||||
## License
|
||||
|
||||
AdminLTE is an open source project by [AdminLTE.io](https://adminlte.io) that is licensed under [MIT](https://opensource.org/licenses/MIT).
|
||||
AdminLTE.io reserves the right to change the license of future releases.
|
||||
|
||||
## Image Credits
|
||||
|
||||
- [Pixeden](http://www.pixeden.com/psd-web-elements/flat-responsive-showcase-psd)
|
||||
- [Graphicsfuel](https://www.graphicsfuel.com/2013/02/13-high-resolution-blur-backgrounds/)
|
||||
- [Pickaface](https://pickaface.net/)
|
||||
- [Unsplash](https://unsplash.com/)
|
||||
- [Uifaces](http://uifaces.com/)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
{
|
||||
"name": "almasaeed2010/adminlte",
|
||||
"description": "AdminLTE - admin control panel and dashboard that's based on Bootstrap 4",
|
||||
"homepage": "https://adminlte.io/",
|
||||
"keywords": [
|
||||
"css",
|
||||
"js",
|
||||
"less",
|
||||
"responsive",
|
||||
"back-end",
|
||||
"template",
|
||||
"theme",
|
||||
"web",
|
||||
"admin"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Colorlib"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"support": {
|
||||
"issues": "https://github.com/ColorlibHQ/AdminLTE/issues"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
BIN
static/admin-lte/dist/assets/img/AdminLTELogo.png
vendored
|
Before Width: | Height: | Size: 2.6 KiB |
BIN
static/admin-lte/dist/assets/img/avatar.png
vendored
|
Before Width: | Height: | Size: 7.9 KiB |
BIN
static/admin-lte/dist/assets/img/avatar2.png
vendored
|
Before Width: | Height: | Size: 8.1 KiB |
BIN
static/admin-lte/dist/assets/img/avatar3.png
vendored
|
Before Width: | Height: | Size: 9 KiB |
BIN
static/admin-lte/dist/assets/img/avatar4.png
vendored
|
Before Width: | Height: | Size: 13 KiB |
BIN
static/admin-lte/dist/assets/img/avatar5.png
vendored
|
Before Width: | Height: | Size: 7.4 KiB |
BIN
static/admin-lte/dist/assets/img/boxed-bg.jpg
vendored
|
Before Width: | Height: | Size: 121 KiB |
BIN
static/admin-lte/dist/assets/img/boxed-bg.png
vendored
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
BIN
static/admin-lte/dist/assets/img/credit/cirrus.png
vendored
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
static/admin-lte/dist/assets/img/credit/paypal.png
vendored
|
Before Width: | Height: | Size: 1.9 KiB |
BIN
static/admin-lte/dist/assets/img/credit/paypal2.png
vendored
|
Before Width: | Height: | Size: 1.2 KiB |
BIN
static/admin-lte/dist/assets/img/credit/visa.png
vendored
|
Before Width: | Height: | Size: 1 KiB |
BIN
static/admin-lte/dist/assets/img/default-150x150.png
vendored
|
Before Width: | Height: | Size: 339 B |
BIN
static/admin-lte/dist/assets/img/icons.png
vendored
|
Before Width: | Height: | Size: 1.1 KiB |
BIN
static/admin-lte/dist/assets/img/photo1.png
vendored
|
Before Width: | Height: | Size: 647 KiB |
BIN
static/admin-lte/dist/assets/img/photo2.png
vendored
|
Before Width: | Height: | Size: 413 KiB |
BIN
static/admin-lte/dist/assets/img/photo3.jpg
vendored
|
Before Width: | Height: | Size: 362 KiB |
BIN
static/admin-lte/dist/assets/img/photo4.jpg
vendored
|
Before Width: | Height: | Size: 1.1 MiB |
BIN
static/admin-lte/dist/assets/img/prod-1.jpg
vendored
|
Before Width: | Height: | Size: 44 KiB |
BIN
static/admin-lte/dist/assets/img/prod-2.jpg
vendored
|
Before Width: | Height: | Size: 32 KiB |
BIN
static/admin-lte/dist/assets/img/prod-3.jpg
vendored
|
Before Width: | Height: | Size: 20 KiB |
BIN
static/admin-lte/dist/assets/img/prod-4.jpg
vendored
|
Before Width: | Height: | Size: 26 KiB |
BIN
static/admin-lte/dist/assets/img/prod-5.jpg
vendored
|
Before Width: | Height: | Size: 31 KiB |
BIN
static/admin-lte/dist/assets/img/user1-128x128.jpg
vendored
|
Before Width: | Height: | Size: 2.7 KiB |
BIN
static/admin-lte/dist/assets/img/user2-160x160.jpg
vendored
|
Before Width: | Height: | Size: 6.7 KiB |
BIN
static/admin-lte/dist/assets/img/user3-128x128.jpg
vendored
|
Before Width: | Height: | Size: 3.3 KiB |
BIN
static/admin-lte/dist/assets/img/user4-128x128.jpg
vendored
|
Before Width: | Height: | Size: 3.4 KiB |
BIN
static/admin-lte/dist/assets/img/user5-128x128.jpg
vendored
|
Before Width: | Height: | Size: 6.3 KiB |
BIN
static/admin-lte/dist/assets/img/user6-128x128.jpg
vendored
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
static/admin-lte/dist/assets/img/user7-128x128.jpg
vendored
|
Before Width: | Height: | Size: 6.2 KiB |
BIN
static/admin-lte/dist/assets/img/user8-128x128.jpg
vendored
|
Before Width: | Height: | Size: 4.9 KiB |
15379
static/admin-lte/dist/css/adminlte.css
vendored
1
static/admin-lte/dist/css/adminlte.css.map
vendored
7
static/admin-lte/dist/css/adminlte.min.css
vendored
15352
static/admin-lte/dist/css/adminlte.rtl.css
vendored
715
static/admin-lte/dist/js/adminlte.js
vendored
|
|
@ -1,715 +0,0 @@
|
|||
/*!
|
||||
* AdminLTE v4.0.0-beta3 (https://adminlte.io)
|
||||
* Copyright 2014-2024 Colorlib <https://colorlib.com>
|
||||
* Licensed under MIT (https://github.com/ColorlibHQ/AdminLTE/blob/master/LICENSE)
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.adminlte = {}));
|
||||
})(this, (function (exports) { 'use strict';
|
||||
|
||||
const domContentLoadedCallbacks = [];
|
||||
const onDOMContentLoaded = (callback) => {
|
||||
if (document.readyState === 'loading') {
|
||||
// add listener on the first call when the document is in loading state
|
||||
if (!domContentLoadedCallbacks.length) {
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
for (const callback of domContentLoadedCallbacks) {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
domContentLoadedCallbacks.push(callback);
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
/* SLIDE UP */
|
||||
const slideUp = (target, duration = 500) => {
|
||||
target.style.transitionProperty = 'height, margin, padding';
|
||||
target.style.transitionDuration = `${duration}ms`;
|
||||
target.style.boxSizing = 'border-box';
|
||||
target.style.height = `${target.offsetHeight}px`;
|
||||
target.style.overflow = 'hidden';
|
||||
window.setTimeout(() => {
|
||||
target.style.height = '0';
|
||||
target.style.paddingTop = '0';
|
||||
target.style.paddingBottom = '0';
|
||||
target.style.marginTop = '0';
|
||||
target.style.marginBottom = '0';
|
||||
}, 1);
|
||||
window.setTimeout(() => {
|
||||
target.style.display = 'none';
|
||||
target.style.removeProperty('height');
|
||||
target.style.removeProperty('padding-top');
|
||||
target.style.removeProperty('padding-bottom');
|
||||
target.style.removeProperty('margin-top');
|
||||
target.style.removeProperty('margin-bottom');
|
||||
target.style.removeProperty('overflow');
|
||||
target.style.removeProperty('transition-duration');
|
||||
target.style.removeProperty('transition-property');
|
||||
}, duration);
|
||||
};
|
||||
/* SLIDE DOWN */
|
||||
const slideDown = (target, duration = 500) => {
|
||||
target.style.removeProperty('display');
|
||||
let { display } = window.getComputedStyle(target);
|
||||
if (display === 'none') {
|
||||
display = 'block';
|
||||
}
|
||||
target.style.display = display;
|
||||
const height = target.offsetHeight;
|
||||
target.style.overflow = 'hidden';
|
||||
target.style.height = '0';
|
||||
target.style.paddingTop = '0';
|
||||
target.style.paddingBottom = '0';
|
||||
target.style.marginTop = '0';
|
||||
target.style.marginBottom = '0';
|
||||
window.setTimeout(() => {
|
||||
target.style.boxSizing = 'border-box';
|
||||
target.style.transitionProperty = 'height, margin, padding';
|
||||
target.style.transitionDuration = `${duration}ms`;
|
||||
target.style.height = `${height}px`;
|
||||
target.style.removeProperty('padding-top');
|
||||
target.style.removeProperty('padding-bottom');
|
||||
target.style.removeProperty('margin-top');
|
||||
target.style.removeProperty('margin-bottom');
|
||||
}, 1);
|
||||
window.setTimeout(() => {
|
||||
target.style.removeProperty('height');
|
||||
target.style.removeProperty('overflow');
|
||||
target.style.removeProperty('transition-duration');
|
||||
target.style.removeProperty('transition-property');
|
||||
}, duration);
|
||||
};
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE layout.ts
|
||||
* @description Layout for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
const CLASS_NAME_HOLD_TRANSITIONS = 'hold-transition';
|
||||
const CLASS_NAME_APP_LOADED = 'app-loaded';
|
||||
/**
|
||||
* Class Definition
|
||||
* ====================================================
|
||||
*/
|
||||
class Layout {
|
||||
constructor(element) {
|
||||
this._element = element;
|
||||
}
|
||||
holdTransition() {
|
||||
let resizeTimer;
|
||||
window.addEventListener('resize', () => {
|
||||
document.body.classList.add(CLASS_NAME_HOLD_TRANSITIONS);
|
||||
clearTimeout(resizeTimer);
|
||||
resizeTimer = setTimeout(() => {
|
||||
document.body.classList.remove(CLASS_NAME_HOLD_TRANSITIONS);
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
}
|
||||
onDOMContentLoaded(() => {
|
||||
const data = new Layout(document.body);
|
||||
data.holdTransition();
|
||||
setTimeout(() => {
|
||||
document.body.classList.add(CLASS_NAME_APP_LOADED);
|
||||
}, 400);
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE push-menu.ts
|
||||
* @description Push menu for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
const DATA_KEY$4 = 'lte.push-menu';
|
||||
const EVENT_KEY$4 = `.${DATA_KEY$4}`;
|
||||
const EVENT_OPEN = `open${EVENT_KEY$4}`;
|
||||
const EVENT_COLLAPSE = `collapse${EVENT_KEY$4}`;
|
||||
const CLASS_NAME_SIDEBAR_MINI = 'sidebar-mini';
|
||||
const CLASS_NAME_SIDEBAR_COLLAPSE = 'sidebar-collapse';
|
||||
const CLASS_NAME_SIDEBAR_OPEN = 'sidebar-open';
|
||||
const CLASS_NAME_SIDEBAR_EXPAND = 'sidebar-expand';
|
||||
const CLASS_NAME_SIDEBAR_OVERLAY = 'sidebar-overlay';
|
||||
const CLASS_NAME_MENU_OPEN$1 = 'menu-open';
|
||||
const SELECTOR_APP_SIDEBAR = '.app-sidebar';
|
||||
const SELECTOR_SIDEBAR_MENU = '.sidebar-menu';
|
||||
const SELECTOR_NAV_ITEM$1 = '.nav-item';
|
||||
const SELECTOR_NAV_TREEVIEW = '.nav-treeview';
|
||||
const SELECTOR_APP_WRAPPER = '.app-wrapper';
|
||||
const SELECTOR_SIDEBAR_EXPAND = `[class*="${CLASS_NAME_SIDEBAR_EXPAND}"]`;
|
||||
const SELECTOR_SIDEBAR_TOGGLE = '[data-lte-toggle="sidebar"]';
|
||||
const Defaults = {
|
||||
sidebarBreakpoint: 992
|
||||
};
|
||||
/**
|
||||
* Class Definition
|
||||
* ====================================================
|
||||
*/
|
||||
class PushMenu {
|
||||
constructor(element, config) {
|
||||
this._element = element;
|
||||
this._config = Object.assign(Object.assign({}, Defaults), config);
|
||||
}
|
||||
// TODO
|
||||
menusClose() {
|
||||
const navTreeview = document.querySelectorAll(SELECTOR_NAV_TREEVIEW);
|
||||
navTreeview.forEach(navTree => {
|
||||
navTree.style.removeProperty('display');
|
||||
navTree.style.removeProperty('height');
|
||||
});
|
||||
const navSidebar = document.querySelector(SELECTOR_SIDEBAR_MENU);
|
||||
const navItem = navSidebar === null || navSidebar === void 0 ? void 0 : navSidebar.querySelectorAll(SELECTOR_NAV_ITEM$1);
|
||||
if (navItem) {
|
||||
navItem.forEach(navI => {
|
||||
navI.classList.remove(CLASS_NAME_MENU_OPEN$1);
|
||||
});
|
||||
}
|
||||
}
|
||||
expand() {
|
||||
const event = new Event(EVENT_OPEN);
|
||||
document.body.classList.remove(CLASS_NAME_SIDEBAR_COLLAPSE);
|
||||
document.body.classList.add(CLASS_NAME_SIDEBAR_OPEN);
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
collapse() {
|
||||
const event = new Event(EVENT_COLLAPSE);
|
||||
document.body.classList.remove(CLASS_NAME_SIDEBAR_OPEN);
|
||||
document.body.classList.add(CLASS_NAME_SIDEBAR_COLLAPSE);
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
addSidebarBreakPoint() {
|
||||
var _a, _b, _c;
|
||||
const sidebarExpandList = (_b = (_a = document.querySelector(SELECTOR_SIDEBAR_EXPAND)) === null || _a === void 0 ? void 0 : _a.classList) !== null && _b !== void 0 ? _b : [];
|
||||
const sidebarExpand = (_c = Array.from(sidebarExpandList).find(className => className.startsWith(CLASS_NAME_SIDEBAR_EXPAND))) !== null && _c !== void 0 ? _c : '';
|
||||
const sidebar = document.getElementsByClassName(sidebarExpand)[0];
|
||||
const sidebarContent = window.getComputedStyle(sidebar, '::before').getPropertyValue('content');
|
||||
this._config = Object.assign(Object.assign({}, this._config), { sidebarBreakpoint: Number(sidebarContent.replace(/[^\d.-]/g, '')) });
|
||||
if (window.innerWidth <= this._config.sidebarBreakpoint) {
|
||||
this.collapse();
|
||||
}
|
||||
else {
|
||||
if (!document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI)) {
|
||||
this.expand();
|
||||
}
|
||||
if (document.body.classList.contains(CLASS_NAME_SIDEBAR_MINI) && document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
|
||||
this.collapse();
|
||||
}
|
||||
}
|
||||
}
|
||||
toggle() {
|
||||
if (document.body.classList.contains(CLASS_NAME_SIDEBAR_COLLAPSE)) {
|
||||
this.expand();
|
||||
}
|
||||
else {
|
||||
this.collapse();
|
||||
}
|
||||
}
|
||||
init() {
|
||||
this.addSidebarBreakPoint();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
onDOMContentLoaded(() => {
|
||||
var _a;
|
||||
const sidebar = document === null || document === void 0 ? void 0 : document.querySelector(SELECTOR_APP_SIDEBAR);
|
||||
if (sidebar) {
|
||||
const data = new PushMenu(sidebar, Defaults);
|
||||
data.init();
|
||||
window.addEventListener('resize', () => {
|
||||
data.init();
|
||||
});
|
||||
}
|
||||
const sidebarOverlay = document.createElement('div');
|
||||
sidebarOverlay.className = CLASS_NAME_SIDEBAR_OVERLAY;
|
||||
(_a = document.querySelector(SELECTOR_APP_WRAPPER)) === null || _a === void 0 ? void 0 : _a.append(sidebarOverlay);
|
||||
sidebarOverlay.addEventListener('touchstart', event => {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget;
|
||||
const data = new PushMenu(target, Defaults);
|
||||
data.collapse();
|
||||
}, { passive: true });
|
||||
sidebarOverlay.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.currentTarget;
|
||||
const data = new PushMenu(target, Defaults);
|
||||
data.collapse();
|
||||
});
|
||||
const fullBtn = document.querySelectorAll(SELECTOR_SIDEBAR_TOGGLE);
|
||||
fullBtn.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
let button = event.currentTarget;
|
||||
if ((button === null || button === void 0 ? void 0 : button.dataset.lteToggle) !== 'sidebar') {
|
||||
button = button === null || button === void 0 ? void 0 : button.closest(SELECTOR_SIDEBAR_TOGGLE);
|
||||
}
|
||||
if (button) {
|
||||
event === null || event === void 0 ? void 0 : event.preventDefault();
|
||||
const data = new PushMenu(button, Defaults);
|
||||
data.toggle();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE treeview.ts
|
||||
* @description Treeview plugin for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Constants
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
// const NAME = 'Treeview'
|
||||
const DATA_KEY$3 = 'lte.treeview';
|
||||
const EVENT_KEY$3 = `.${DATA_KEY$3}`;
|
||||
const EVENT_EXPANDED$2 = `expanded${EVENT_KEY$3}`;
|
||||
const EVENT_COLLAPSED$2 = `collapsed${EVENT_KEY$3}`;
|
||||
// const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
|
||||
const CLASS_NAME_MENU_OPEN = 'menu-open';
|
||||
const SELECTOR_NAV_ITEM = '.nav-item';
|
||||
const SELECTOR_NAV_LINK = '.nav-link';
|
||||
const SELECTOR_TREEVIEW_MENU = '.nav-treeview';
|
||||
const SELECTOR_DATA_TOGGLE$1 = '[data-lte-toggle="treeview"]';
|
||||
const Default$1 = {
|
||||
animationSpeed: 300,
|
||||
accordion: true
|
||||
};
|
||||
/**
|
||||
* Class Definition
|
||||
* ====================================================
|
||||
*/
|
||||
class Treeview {
|
||||
constructor(element, config) {
|
||||
this._element = element;
|
||||
this._config = Object.assign(Object.assign({}, Default$1), config);
|
||||
}
|
||||
open() {
|
||||
var _a, _b;
|
||||
const event = new Event(EVENT_EXPANDED$2);
|
||||
if (this._config.accordion) {
|
||||
const openMenuList = (_a = this._element.parentElement) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_NAV_ITEM}.${CLASS_NAME_MENU_OPEN}`);
|
||||
openMenuList === null || openMenuList === void 0 ? void 0 : openMenuList.forEach(openMenu => {
|
||||
if (openMenu !== this._element.parentElement) {
|
||||
openMenu.classList.remove(CLASS_NAME_MENU_OPEN);
|
||||
const childElement = openMenu === null || openMenu === void 0 ? void 0 : openMenu.querySelector(SELECTOR_TREEVIEW_MENU);
|
||||
if (childElement) {
|
||||
slideUp(childElement, this._config.animationSpeed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this._element.classList.add(CLASS_NAME_MENU_OPEN);
|
||||
const childElement = (_b = this._element) === null || _b === void 0 ? void 0 : _b.querySelector(SELECTOR_TREEVIEW_MENU);
|
||||
if (childElement) {
|
||||
slideDown(childElement, this._config.animationSpeed);
|
||||
}
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
close() {
|
||||
var _a;
|
||||
const event = new Event(EVENT_COLLAPSED$2);
|
||||
this._element.classList.remove(CLASS_NAME_MENU_OPEN);
|
||||
const childElement = (_a = this._element) === null || _a === void 0 ? void 0 : _a.querySelector(SELECTOR_TREEVIEW_MENU);
|
||||
if (childElement) {
|
||||
slideUp(childElement, this._config.animationSpeed);
|
||||
}
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
toggle() {
|
||||
if (this._element.classList.contains(CLASS_NAME_MENU_OPEN)) {
|
||||
this.close();
|
||||
}
|
||||
else {
|
||||
this.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* ------------------------------------------------------------------------
|
||||
* Data Api implementation
|
||||
* ------------------------------------------------------------------------
|
||||
*/
|
||||
onDOMContentLoaded(() => {
|
||||
const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE$1);
|
||||
button.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
const target = event.target;
|
||||
const targetItem = target.closest(SELECTOR_NAV_ITEM);
|
||||
const targetLink = target.closest(SELECTOR_NAV_LINK);
|
||||
if ((target === null || target === void 0 ? void 0 : target.getAttribute('href')) === '#' || (targetLink === null || targetLink === void 0 ? void 0 : targetLink.getAttribute('href')) === '#') {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (targetItem) {
|
||||
const data = new Treeview(targetItem, Default$1);
|
||||
data.toggle();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE direct-chat.ts
|
||||
* @description Direct chat for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* Constants
|
||||
* ====================================================
|
||||
*/
|
||||
const DATA_KEY$2 = 'lte.direct-chat';
|
||||
const EVENT_KEY$2 = `.${DATA_KEY$2}`;
|
||||
const EVENT_EXPANDED$1 = `expanded${EVENT_KEY$2}`;
|
||||
const EVENT_COLLAPSED$1 = `collapsed${EVENT_KEY$2}`;
|
||||
const SELECTOR_DATA_TOGGLE = '[data-lte-toggle="chat-pane"]';
|
||||
const SELECTOR_DIRECT_CHAT = '.direct-chat';
|
||||
const CLASS_NAME_DIRECT_CHAT_OPEN = 'direct-chat-contacts-open';
|
||||
/**
|
||||
* Class Definition
|
||||
* ====================================================
|
||||
*/
|
||||
class DirectChat {
|
||||
constructor(element) {
|
||||
this._element = element;
|
||||
}
|
||||
toggle() {
|
||||
if (this._element.classList.contains(CLASS_NAME_DIRECT_CHAT_OPEN)) {
|
||||
const event = new Event(EVENT_COLLAPSED$1);
|
||||
this._element.classList.remove(CLASS_NAME_DIRECT_CHAT_OPEN);
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
else {
|
||||
const event = new Event(EVENT_EXPANDED$1);
|
||||
this._element.classList.add(CLASS_NAME_DIRECT_CHAT_OPEN);
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Data Api implementation
|
||||
* ====================================================
|
||||
*/
|
||||
onDOMContentLoaded(() => {
|
||||
const button = document.querySelectorAll(SELECTOR_DATA_TOGGLE);
|
||||
button.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const chatPane = target.closest(SELECTOR_DIRECT_CHAT);
|
||||
if (chatPane) {
|
||||
const data = new DirectChat(chatPane);
|
||||
data.toggle();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE card-widget.ts
|
||||
* @description Card widget for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* Constants
|
||||
* ====================================================
|
||||
*/
|
||||
const DATA_KEY$1 = 'lte.card-widget';
|
||||
const EVENT_KEY$1 = `.${DATA_KEY$1}`;
|
||||
const EVENT_COLLAPSED = `collapsed${EVENT_KEY$1}`;
|
||||
const EVENT_EXPANDED = `expanded${EVENT_KEY$1}`;
|
||||
const EVENT_REMOVE = `remove${EVENT_KEY$1}`;
|
||||
const EVENT_MAXIMIZED$1 = `maximized${EVENT_KEY$1}`;
|
||||
const EVENT_MINIMIZED$1 = `minimized${EVENT_KEY$1}`;
|
||||
const CLASS_NAME_CARD = 'card';
|
||||
const CLASS_NAME_COLLAPSED = 'collapsed-card';
|
||||
const CLASS_NAME_COLLAPSING = 'collapsing-card';
|
||||
const CLASS_NAME_EXPANDING = 'expanding-card';
|
||||
const CLASS_NAME_WAS_COLLAPSED = 'was-collapsed';
|
||||
const CLASS_NAME_MAXIMIZED = 'maximized-card';
|
||||
const SELECTOR_DATA_REMOVE = '[data-lte-toggle="card-remove"]';
|
||||
const SELECTOR_DATA_COLLAPSE = '[data-lte-toggle="card-collapse"]';
|
||||
const SELECTOR_DATA_MAXIMIZE = '[data-lte-toggle="card-maximize"]';
|
||||
const SELECTOR_CARD = `.${CLASS_NAME_CARD}`;
|
||||
const SELECTOR_CARD_BODY = '.card-body';
|
||||
const SELECTOR_CARD_FOOTER = '.card-footer';
|
||||
const Default = {
|
||||
animationSpeed: 500,
|
||||
collapseTrigger: SELECTOR_DATA_COLLAPSE,
|
||||
removeTrigger: SELECTOR_DATA_REMOVE,
|
||||
maximizeTrigger: SELECTOR_DATA_MAXIMIZE
|
||||
};
|
||||
class CardWidget {
|
||||
constructor(element, config) {
|
||||
this._element = element;
|
||||
this._parent = element.closest(SELECTOR_CARD);
|
||||
if (element.classList.contains(CLASS_NAME_CARD)) {
|
||||
this._parent = element;
|
||||
}
|
||||
this._config = Object.assign(Object.assign({}, Default), config);
|
||||
}
|
||||
collapse() {
|
||||
var _a, _b;
|
||||
const event = new Event(EVENT_COLLAPSED);
|
||||
if (this._parent) {
|
||||
this._parent.classList.add(CLASS_NAME_COLLAPSING);
|
||||
const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
|
||||
elm.forEach(el => {
|
||||
if (el instanceof HTMLElement) {
|
||||
slideUp(el, this._config.animationSpeed);
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
if (this._parent) {
|
||||
this._parent.classList.add(CLASS_NAME_COLLAPSED);
|
||||
this._parent.classList.remove(CLASS_NAME_COLLAPSING);
|
||||
}
|
||||
}, this._config.animationSpeed);
|
||||
}
|
||||
(_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
|
||||
}
|
||||
expand() {
|
||||
var _a, _b;
|
||||
const event = new Event(EVENT_EXPANDED);
|
||||
if (this._parent) {
|
||||
this._parent.classList.add(CLASS_NAME_EXPANDING);
|
||||
const elm = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.querySelectorAll(`${SELECTOR_CARD_BODY}, ${SELECTOR_CARD_FOOTER}`);
|
||||
elm.forEach(el => {
|
||||
if (el instanceof HTMLElement) {
|
||||
slideDown(el, this._config.animationSpeed);
|
||||
}
|
||||
});
|
||||
setTimeout(() => {
|
||||
if (this._parent) {
|
||||
this._parent.classList.remove(CLASS_NAME_COLLAPSED);
|
||||
this._parent.classList.remove(CLASS_NAME_EXPANDING);
|
||||
}
|
||||
}, this._config.animationSpeed);
|
||||
}
|
||||
(_b = this._element) === null || _b === void 0 ? void 0 : _b.dispatchEvent(event);
|
||||
}
|
||||
remove() {
|
||||
var _a;
|
||||
const event = new Event(EVENT_REMOVE);
|
||||
if (this._parent) {
|
||||
slideUp(this._parent, this._config.animationSpeed);
|
||||
}
|
||||
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
|
||||
}
|
||||
toggle() {
|
||||
var _a;
|
||||
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_COLLAPSED)) {
|
||||
this.expand();
|
||||
return;
|
||||
}
|
||||
this.collapse();
|
||||
}
|
||||
maximize() {
|
||||
var _a;
|
||||
const event = new Event(EVENT_MAXIMIZED$1);
|
||||
if (this._parent) {
|
||||
this._parent.style.height = `${this._parent.offsetHeight}px`;
|
||||
this._parent.style.width = `${this._parent.offsetWidth}px`;
|
||||
this._parent.style.transition = 'all .15s';
|
||||
setTimeout(() => {
|
||||
const htmlTag = document.querySelector('html');
|
||||
if (htmlTag) {
|
||||
htmlTag.classList.add(CLASS_NAME_MAXIMIZED);
|
||||
}
|
||||
if (this._parent) {
|
||||
this._parent.classList.add(CLASS_NAME_MAXIMIZED);
|
||||
if (this._parent.classList.contains(CLASS_NAME_COLLAPSED)) {
|
||||
this._parent.classList.add(CLASS_NAME_WAS_COLLAPSED);
|
||||
}
|
||||
}
|
||||
}, 150);
|
||||
}
|
||||
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
|
||||
}
|
||||
minimize() {
|
||||
var _a;
|
||||
const event = new Event(EVENT_MINIMIZED$1);
|
||||
if (this._parent) {
|
||||
this._parent.style.height = 'auto';
|
||||
this._parent.style.width = 'auto';
|
||||
this._parent.style.transition = 'all .15s';
|
||||
setTimeout(() => {
|
||||
var _a;
|
||||
const htmlTag = document.querySelector('html');
|
||||
if (htmlTag) {
|
||||
htmlTag.classList.remove(CLASS_NAME_MAXIMIZED);
|
||||
}
|
||||
if (this._parent) {
|
||||
this._parent.classList.remove(CLASS_NAME_MAXIMIZED);
|
||||
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_WAS_COLLAPSED)) {
|
||||
this._parent.classList.remove(CLASS_NAME_WAS_COLLAPSED);
|
||||
}
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
(_a = this._element) === null || _a === void 0 ? void 0 : _a.dispatchEvent(event);
|
||||
}
|
||||
toggleMaximize() {
|
||||
var _a;
|
||||
if ((_a = this._parent) === null || _a === void 0 ? void 0 : _a.classList.contains(CLASS_NAME_MAXIMIZED)) {
|
||||
this.minimize();
|
||||
return;
|
||||
}
|
||||
this.maximize();
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* Data Api implementation
|
||||
* ====================================================
|
||||
*/
|
||||
onDOMContentLoaded(() => {
|
||||
const collapseBtn = document.querySelectorAll(SELECTOR_DATA_COLLAPSE);
|
||||
collapseBtn.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const data = new CardWidget(target, Default);
|
||||
data.toggle();
|
||||
});
|
||||
});
|
||||
const removeBtn = document.querySelectorAll(SELECTOR_DATA_REMOVE);
|
||||
removeBtn.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const data = new CardWidget(target, Default);
|
||||
data.remove();
|
||||
});
|
||||
});
|
||||
const maxBtn = document.querySelectorAll(SELECTOR_DATA_MAXIMIZE);
|
||||
maxBtn.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const data = new CardWidget(target, Default);
|
||||
data.toggleMaximize();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* --------------------------------------------
|
||||
* @file AdminLTE fullscreen.ts
|
||||
* @description Fullscreen plugin for AdminLTE.
|
||||
* @license MIT
|
||||
* --------------------------------------------
|
||||
*/
|
||||
/**
|
||||
* Constants
|
||||
* ============================================================================
|
||||
*/
|
||||
const DATA_KEY = 'lte.fullscreen';
|
||||
const EVENT_KEY = `.${DATA_KEY}`;
|
||||
const EVENT_MAXIMIZED = `maximized${EVENT_KEY}`;
|
||||
const EVENT_MINIMIZED = `minimized${EVENT_KEY}`;
|
||||
const SELECTOR_FULLSCREEN_TOGGLE = '[data-lte-toggle="fullscreen"]';
|
||||
const SELECTOR_MAXIMIZE_ICON = '[data-lte-icon="maximize"]';
|
||||
const SELECTOR_MINIMIZE_ICON = '[data-lte-icon="minimize"]';
|
||||
/**
|
||||
* Class Definition.
|
||||
* ============================================================================
|
||||
*/
|
||||
class FullScreen {
|
||||
constructor(element, config) {
|
||||
this._element = element;
|
||||
this._config = config;
|
||||
}
|
||||
inFullScreen() {
|
||||
const event = new Event(EVENT_MAXIMIZED);
|
||||
const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
|
||||
const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
|
||||
void document.documentElement.requestFullscreen();
|
||||
if (iconMaximize) {
|
||||
iconMaximize.style.display = 'none';
|
||||
}
|
||||
if (iconMinimize) {
|
||||
iconMinimize.style.display = 'block';
|
||||
}
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
outFullscreen() {
|
||||
const event = new Event(EVENT_MINIMIZED);
|
||||
const iconMaximize = document.querySelector(SELECTOR_MAXIMIZE_ICON);
|
||||
const iconMinimize = document.querySelector(SELECTOR_MINIMIZE_ICON);
|
||||
void document.exitFullscreen();
|
||||
if (iconMaximize) {
|
||||
iconMaximize.style.display = 'block';
|
||||
}
|
||||
if (iconMinimize) {
|
||||
iconMinimize.style.display = 'none';
|
||||
}
|
||||
this._element.dispatchEvent(event);
|
||||
}
|
||||
toggleFullScreen() {
|
||||
if (document.fullscreenEnabled) {
|
||||
if (document.fullscreenElement) {
|
||||
this.outFullscreen();
|
||||
}
|
||||
else {
|
||||
this.inFullScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Data Api implementation
|
||||
* ============================================================================
|
||||
*/
|
||||
onDOMContentLoaded(() => {
|
||||
const buttons = document.querySelectorAll(SELECTOR_FULLSCREEN_TOGGLE);
|
||||
buttons.forEach(btn => {
|
||||
btn.addEventListener('click', event => {
|
||||
event.preventDefault();
|
||||
const target = event.target;
|
||||
const button = target.closest(SELECTOR_FULLSCREEN_TOGGLE);
|
||||
if (button) {
|
||||
const data = new FullScreen(button, undefined);
|
||||
data.toggleFullScreen();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
exports.CardWidget = CardWidget;
|
||||
exports.DirectChat = DirectChat;
|
||||
exports.FullScreen = FullScreen;
|
||||
exports.Layout = Layout;
|
||||
exports.PushMenu = PushMenu;
|
||||
exports.Treeview = Treeview;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=adminlte.js.map
|
||||