├── .gitignore
├── README.md
├── babel.config.js
├── package.json
├── public
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── logo.png
│ └── logo.svg
├── components
│ ├── UserAuthForm.vue
│ ├── VideoEditForm.vue
│ ├── VideoListVideo.vue
│ └── VuetifyHelloWorld.vue
├── main.js
├── plugins
│ └── vuetify.js
├── router
│ └── index.js
├── services
│ └── api.js
├── store
│ ├── index.js
│ ├── snackbar.js
│ ├── tags.js
│ ├── users.js
│ └── videos.js
├── utils
│ └── validations.js
└── views
│ ├── About.vue
│ ├── Admin.vue
│ ├── AdminTagList.vue
│ ├── AdminUserList.vue
│ ├── AdminVideoCreate.vue
│ ├── AdminVideoEdit.vue
│ ├── AdminVideoList.vue
│ ├── AdminVideoShow.vue
│ ├── Home.vue
│ ├── TagVideoList.vue
│ ├── UserLogin.vue
│ ├── UserRegistration.vue
│ └── VideoWatch.vue
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-screencasts
2 |
3 | ## Project setup
4 | ```
5 | yarn install
6 | ```
7 |
8 | ### Compiles and hot-reloads for development
9 | ```
10 | yarn serve
11 | ```
12 |
13 | ### Compiles and minifies for production
14 | ```
15 | yarn build
16 | ```
17 |
18 | ### Lints and fixes files
19 | ```
20 | yarn lint
21 | ```
22 |
23 | ### Customize configuration
24 | See [Configuration Reference](https://cli.vuejs.org/config/).
25 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/cli-plugin-babel/preset"]
3 | };
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-screencasts",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "@fortawesome/fontawesome-svg-core": "^1.2.22",
12 | "@fortawesome/free-solid-svg-icons": "^5.10.2",
13 | "@fortawesome/vue-fontawesome": "^0.1.7",
14 | "axios": "^0.19.0",
15 | "core-js": "^3.1.2",
16 | "vue": "^2.6.10",
17 | "vue-router": "^3.0.6",
18 | "vue-video-player": "^5.0.2",
19 | "vuetify": "^2.0.0",
20 | "vuex": "^3.0.1"
21 | },
22 | "devDependencies": {
23 | "@mdi/font": "^4.4.95",
24 | "@vue/cli-plugin-babel": "^4.0.0-rc.2",
25 | "@vue/cli-plugin-eslint": "^4.0.0-rc.2",
26 | "@vue/cli-plugin-router": "^4.0.0-rc.2",
27 | "@vue/cli-plugin-vuex": "^4.0.0-rc.2",
28 | "@vue/cli-service": "^4.0.0-rc.2",
29 | "babel-eslint": "^10.0.1",
30 | "eslint": "^5.16.0",
31 | "eslint-plugin-vue": "^5.0.0",
32 | "sass": "^1.19.0",
33 | "sass-loader": "^7.1.0",
34 | "vue-cli-plugin-vuetify": "^0.6.3",
35 | "vue-template-compiler": "^2.6.10",
36 | "vuetify-loader": "^1.2.2"
37 | },
38 | "eslintConfig": {
39 | "root": true,
40 | "env": {
41 | "node": true
42 | },
43 | "extends": [
44 | "plugin:vue/essential"
45 | ],
46 | "rules": {},
47 | "parserOptions": {
48 | "parser": "babel-eslint"
49 | }
50 | },
51 | "postcss": {
52 | "plugins": {
53 | "autoprefixer": {}
54 | }
55 | },
56 | "browserslist": [
57 | "> 1%",
58 | "last 2 versions"
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffreybiles/vue-screencasts/552011f9f5dcbbf40bdb91a8bc75afc3e67d7259/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-screencasts
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vue Screencasts
6 |
7 | Admin
8 |
9 |
10 | {{ currentUser.name }}
11 | Logout
12 |
13 |
14 | Login
15 | Register
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
31 | {{snackbar.text}}
32 |
33 |
34 | Close
35 |
36 |
37 |
38 |
39 |
40 |
67 |
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jeffreybiles/vue-screencasts/552011f9f5dcbbf40bdb91a8bc75afc3e67d7259/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/components/UserAuthForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
11 |
12 |
20 |
21 | {{ buttonText }}
22 |
23 |
24 |
25 |
43 |
44 |
--------------------------------------------------------------------------------
/src/components/VideoEditForm.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
15 |
18 |
19 | {{buttonText}}
20 |
21 |
22 |
23 |
36 |
37 |
--------------------------------------------------------------------------------
/src/components/VideoListVideo.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | {{ video.name }}
8 |
9 |
10 | Played
11 |
12 |
13 |
14 |
15 |
16 |
21 | {{ getTag(tag_id).name }}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/VuetifyHelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
14 |
15 |
16 |
17 |
18 | Welcome to Vuetify
19 |
20 |
21 | For help and collaboration with other Vuetify developers,
22 |
please join our online
23 | Discord Community
24 |
25 |
26 |
27 |
31 | What's next?
32 |
33 |
34 |
41 | {{ next.text }}
42 |
43 |
44 |
45 |
46 |
50 | Important Links
51 |
52 |
53 |
60 | {{ link.text }}
61 |
62 |
63 |
64 |
65 |
69 | Ecosystem
70 |
71 |
72 |
79 | {{ eco.text }}
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
143 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 | import store from "./store";
5 | import vuetify from './plugins/vuetify';
6 |
7 | import { library } from '@fortawesome/fontawesome-svg-core'
8 | import { faCheck } from '@fortawesome/free-solid-svg-icons'
9 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
10 |
11 | library.add(faCheck)
12 |
13 | Vue.component('font-awesome-icon', FontAwesomeIcon)
14 |
15 | Vue.config.productionTip = false;
16 |
17 | new Vue({
18 | router,
19 | store,
20 | vuetify,
21 | render: h => h(App)
22 | }).$mount("#app");
23 |
--------------------------------------------------------------------------------
/src/plugins/vuetify.js:
--------------------------------------------------------------------------------
1 | import '@mdi/font/css/materialdesignicons.css'
2 | import Vue from 'vue';
3 | import Vuetify from 'vuetify/lib';
4 |
5 | Vue.use(Vuetify);
6 |
7 | export default new Vuetify({
8 | icons: {
9 | iconfont: 'mdi',
10 | },
11 | });
12 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import VueRouter from "vue-router";
3 | import Home from "../views/Home.vue";
4 | import VideoWatch from "../views/VideoWatch.vue";
5 | import AdminVideoCreate from "../views/AdminVideoCreate.vue";
6 | import TagVideoList from "../views/TagVideoList.vue";
7 | import AdminVideoList from "../views/AdminVideoList.vue";
8 | import AdminVideoEdit from "../views/AdminVideoEdit.vue";
9 | import AdminUserList from "../views/AdminUserList.vue";
10 | import UserLogin from "../views/UserLogin.vue";
11 | import UserRegistration from "../views/UserRegistration.vue";
12 | import Admin from "../views/Admin.vue";
13 | import AdminVideoShow from "../views/AdminVideoShow.vue";
14 | import AdminTagList from "../views/AdminTagList.vue";
15 |
16 | Vue.use(VueRouter);
17 |
18 | const routes = [
19 | {
20 | path: "/",
21 | name: "home",
22 | component: Home
23 | },
24 | {
25 | path: "/admin",
26 | name: "admin",
27 | component: Admin,
28 | beforeEnter(to, from, next) {
29 | let currentUser = JSON.parse(window.localStorage.currentUser);
30 | if(currentUser && currentUser.admin) {
31 | next();
32 | } else {
33 | next("/");
34 | }
35 | },
36 | children: [
37 | {
38 | path: "videos",
39 | name: "admin-video-list",
40 | component: AdminVideoList,
41 | },
42 | {
43 | path: "users",
44 | name: "admin-user-list",
45 | component: AdminUserList
46 | },
47 | {
48 | path: "videos/:id/edit",
49 | name: "admin-video-edit",
50 | component: AdminVideoEdit,
51 | params: true,
52 | },
53 | {
54 | path: "video/new",
55 | name: "admin-video-create",
56 | component: AdminVideoCreate
57 | },
58 | {
59 | path: "videos/:id/show",
60 | name: "admin-video-show",
61 | component: AdminVideoShow,
62 | params: true,
63 | },
64 | {
65 | path: "tags",
66 | name: "admin-tag-list",
67 | component: AdminTagList,
68 | }
69 | ]
70 | },
71 | {
72 | path: '/login',
73 | name: 'user-login',
74 | component: UserLogin
75 | },
76 | {
77 | path: '/registration',
78 | name: 'user-registration',
79 | component: UserRegistration
80 | },
81 | {
82 | path: "/about",
83 | name: "about",
84 | // route level code-splitting
85 | // this generates a separate chunk (about.[hash].js) for this route
86 | // which is lazy-loaded when the route is visited.
87 | component: () =>
88 | import(/* webpackChunkName: "about" */ "../views/About.vue")
89 | },
90 | {
91 | path: "/video/:id",
92 | name: "video-watch",
93 | component: VideoWatch,
94 | params: true
95 | },
96 | {
97 | path: "/tag/:id",
98 | name: "tag",
99 | component: TagVideoList,
100 | params: true
101 | }
102 | ];
103 |
104 | const router = new VueRouter({
105 | routes
106 | });
107 |
108 | export default router;
109 |
--------------------------------------------------------------------------------
/src/services/api.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios';
2 |
3 | export default () => {
4 | let currentUserString = window.localStorage.currentUser;
5 | let currentUser = currentUserString ? JSON.parse(currentUserString) : '';
6 | return axios.create({
7 | baseURL: 'http://localhost:3000/api',
8 | withCredentials: false,
9 | headers: {
10 | Accept: "application/json",
11 | "Content-Type": "application/json",
12 | Authorization: currentUser && currentUser.token
13 | }
14 | });
15 | }
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 | import snackbarModule from './snackbar';
4 | import tagsModule from './tags';
5 | import videosModule from './videos';
6 | import usersModule from './users';
7 |
8 | Vue.use(Vuex);
9 |
10 | export default new Vuex.Store({
11 | modules: {
12 | snackbar: snackbarModule,
13 | tags: tagsModule,
14 | videos: videosModule,
15 | users: usersModule,
16 | },
17 | });
18 |
--------------------------------------------------------------------------------
/src/store/snackbar.js:
--------------------------------------------------------------------------------
1 | export default {
2 | namespaced: true,
3 | state: {
4 | snackbars: []
5 | },
6 | mutations: {
7 | SET_SNACKBAR(state, snackbar) {
8 | state.snackbars = state.snackbars.concat(snackbar);
9 | },
10 | },
11 | actions: {
12 | setSnackbar({commit}, snackbar) {
13 | snackbar.showing = true;
14 | snackbar.color = snackbar.color || 'success';
15 | commit('SET_SNACKBAR', snackbar);
16 | },
17 | },
18 | getters: {},
19 | modules: {}
20 | }
--------------------------------------------------------------------------------
/src/store/tags.js:
--------------------------------------------------------------------------------
1 | import Api from "@/services/api";
2 |
3 | export default {
4 | namespaced: true,
5 | state: {
6 | tags: []
7 | },
8 | mutations: {
9 | SET_TAGS(state, tags) {
10 | state.tags = tags;
11 | },
12 | CREATE_TAG(state, {tag}) {
13 | state.tags = state.tags.concat(tag);
14 | },
15 | UPDATE_TAG_NAME(state, {tag}) {
16 | let tagToUpdate = state.tags.find(t => t.id == tag.id)
17 | tagToUpdate.name = tag.name
18 | },
19 | DELETE_TAG(state, {tag}) {
20 | state.tags = state.tags.filter(t => t.id != tag.id)
21 | },
22 | CONNECT_TAG_TO_VIDEO(state, {video, tag}) {
23 | video.tag_ids = video.tag_ids.concat(tag.id.toString());
24 | tag.video_ids = tag.video_ids.concat(video.id.toString());
25 | },
26 | DISCONNECT_TAG_FROM_VIDEO(state, {video, tag}) {
27 | video.tag_ids = video.tag_ids.filter(t_id => t_id != tag.id);
28 | tag.video_ids = tag.video_ids.filter(v_id => v_id != video.id);
29 | },
30 | },
31 | actions: {
32 | async loadAll({commit, dispatch}) {
33 | let response = await Api().get('/tags');
34 | let tags = response.data.data;
35 | tags.forEach(t => {
36 | t.attributes.id = t.id;
37 | t.attributes.video_ids = t.relationships.videos.data.map(v => v.id);
38 | });
39 | commit('SET_TAGS', tags.map(t => t.attributes));
40 | },
41 | connectToVideo({commit}, {video, tag}) {
42 | Api().post('/video_tags', {
43 | video_id: video.id,
44 | tag_id: tag.id
45 | });
46 | commit('CONNECT_TAG_TO_VIDEO', {video, tag});
47 | },
48 | disconnectTagFromVideo({commit}, {video, tag}) {
49 | Api().post('video_tags/delete', {
50 | video_id: video.id,
51 | tag_id: tag.id
52 | });
53 | commit('DISCONNECT_TAG_FROM_VIDEO', {video, tag});
54 | },
55 | async create({commit}, {name}) {
56 | let response = await Api().post('/tags', {name});
57 | let createdTag = response.data.data.attributes;
58 | createdTag.id = response.data.data.id;
59 | createdTag.video_ids = [];
60 | commit('CREATE_TAG', {tag: createdTag});
61 | return createdTag;
62 | },
63 | async updateName({commit}, {tag}) {
64 | Api().put(`/tags/${tag.id}`, tag)
65 | commit('UPDATE_TAG_NAME', {tag});
66 | },
67 | delete({commit}, {tag}) {
68 | Api().delete(`/tags/${tag.id}`);
69 | commit('DELETE_TAG', {tag})
70 | }
71 | },
72 | getters: {
73 | get: state => id => {
74 | return state.tags.find(t => t.id == id)
75 | },
76 | }
77 | }
--------------------------------------------------------------------------------
/src/store/users.js:
--------------------------------------------------------------------------------
1 | import Api from "@/services/api";
2 | import Vue from 'vue';
3 |
4 | export default {
5 | namespaced: true,
6 | state: {
7 | users: [],
8 | currentUser: {}
9 | },
10 | mutations: {
11 | SET_PLAYED_VIDEOS(state, playedVideos) {
12 | Vue.set(state.currentUser, 'playedVideos', playedVideos);
13 | },
14 | SET_USERS(state, users) {
15 | state.users = users;
16 | },
17 | MARK_VIDEO_PLAYED(state, videoId) {
18 | let playedVideos = state.currentUser.playedVideos.concat(videoId);
19 | state.currentUser.playedVideos = playedVideos;
20 | },
21 | LOGOUT_USER(state) {
22 | state.currentUser = {}
23 | window.localStorage.currentUser = JSON.stringify({});
24 | },
25 | SET_CURRENT_USER(state, user) {
26 | state.currentUser = user;
27 | window.localStorage.currentUser = JSON.stringify(user);
28 | },
29 | },
30 | actions: {
31 | async loadAll({commit}) {
32 | let response = await Api().get('/users');
33 | let users = response.data.data;
34 | commit('SET_USERS', users.map(u => u.attributes));
35 | },
36 | async loadCurrent({commit, dispatch}) {
37 | let user = JSON.parse(window.localStorage.currentUser);
38 | commit('SET_CURRENT_USER', user);
39 | dispatch('loadPlayedVideos', user.id);
40 | },
41 | async loadPlayedVideos({commit}, userId) {
42 | let response = await Api().get(`users/${userId}`);
43 | let user = response.data.data.attributes;
44 | commit('SET_PLAYED_VIDEOS', user.played_video_ids)
45 | },
46 | markVideoPlayed({commit, state}, videoId) {
47 | if(state.currentUser.name){
48 | commit('MARK_VIDEO_PLAYED', videoId);
49 | Api().post(`/video_plays`, {
50 | video_id: videoId
51 | });
52 | }
53 | },
54 | logout({commit}) {
55 | commit('LOGOUT_USER');
56 | },
57 | async login({commit, dispatch}, loginInfo) {
58 | try {
59 | let response = await Api().post('/sessions', loginInfo);
60 | let user = response.data.data.attributes;
61 |
62 | commit('SET_CURRENT_USER', user);
63 | dispatch('loadPlayedVideos', user.id);
64 | return user;
65 | } catch {
66 | return {error: "Email/password combination was incorrect. Please try again."}
67 | }
68 | },
69 | async register({commit, dispatch}, registrationInfo) {
70 | try {
71 | let response = await Api().post('/users', registrationInfo);
72 | let user = response.data.data.attributes;
73 |
74 | commit('SET_CURRENT_USER', user);
75 | dispatch('loadPlayedVideos', user.id);
76 | return user;
77 | } catch {
78 | return { error: "There was an error. Please try again." }
79 | }
80 | },
81 | },
82 | getters: {
83 | playedVideos: state => {
84 | return state.currentUser.playedVideos || [];
85 | },
86 | videoIsPlayed: (state, getters) => videoId => {
87 | return getters.playedVideos.includes(videoId)
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/src/store/videos.js:
--------------------------------------------------------------------------------
1 | import Api from "@/services/api";
2 |
3 | export default {
4 | namespaced: true,
5 | state: {
6 | videos: [],
7 | },
8 | mutations: {
9 | SET_VIDEOS(state, videos) {
10 | state.videos = videos;
11 | },
12 | ADD_VIDEO(state, video) {
13 | let videos = state.videos.concat(video);
14 | state.videos = videos;
15 | },
16 | DELETE_VIDEO(state, videoId){
17 | let videos = state.videos.filter(v => v.id != videoId)
18 | state.videos = videos;
19 | },
20 | EDIT_VIDEO(state, video) {
21 | let v = state.videos.find(v => v.id == video.id)
22 | v = video;
23 | },
24 | },
25 | actions: {
26 | async loadAll({commit, dispatch}){
27 | let response = await Api().get('/videos');
28 | let videos = response.data.data;
29 | videos.forEach(v => {
30 | v.attributes.tag_ids = v.relationships.tags.data.map(t => t.id);
31 | });
32 |
33 | commit('SET_VIDEOS', videos.map(v => v.attributes));
34 | },
35 | async create({commit}, video) {
36 | let response = await Api().post('/videos', video);
37 | let savedVideo = response.data.data.attributes;
38 | commit('ADD_VIDEO', savedVideo);
39 | return savedVideo;
40 | },
41 | async delete({commit}, video) {
42 | let response = await Api().delete(`/videos/${video.id}`);
43 | if(response.status == 200 || response.status == 204){
44 | commit('DELETE_VIDEO', video.id);
45 | }
46 | },
47 | async edit({commit}, video) {
48 | let response = await Api().put(`/videos/${video.id}`, video);
49 | let newVideo = response.data.data.attributes;
50 | commit('EDIT_VIDEO', newVideo);
51 | return newVideo;
52 | },
53 | }
54 | }
--------------------------------------------------------------------------------
/src/utils/validations.js:
--------------------------------------------------------------------------------
1 | let required = (propertyType) => {
2 | return v => v && v.length > 0 || `You must input a ${propertyType}`
3 | }
4 | let minLength = (propertyType, minLength) => {
5 | return v => v && v.length >= minLength || `${propertyType} must be at least ${minLength} characters`
6 | }
7 | let maxLength = (propertyType, maxLength) => {
8 | return v => v && v.length <= maxLength || `${propertyType} must be less than ${maxLength} characters`
9 | }
10 |
11 | let emailFormat = () => {
12 | let regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,24})+$/
13 | return v => v && regex.test(v) || "Must be a valid email"
14 | }
15 |
16 | export default {
17 | required,
18 | minLength,
19 | maxLength,
20 | emailFormat
21 | }
--------------------------------------------------------------------------------
/src/views/About.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
This is an about page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/views/Admin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Videos
5 | Tags
6 | Users
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/src/views/AdminTagList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Name
5 |
# videos
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 | {{tag.name}}
18 |
19 |
20 |
21 |
22 | {{tag.video_ids.length}}
23 |
24 |
25 |
26 | Edit
27 | Delete
28 |
29 |
30 |
31 | Add Tag
32 |
33 |
34 |
38 |
39 |
40 |
41 |
42 |
93 |
94 |
--------------------------------------------------------------------------------
/src/views/AdminUserList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{user.name}} Login
5 |
6 |
7 |
8 |
9 |
28 |
29 |
--------------------------------------------------------------------------------
/src/views/AdminVideoCreate.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Video Create Page
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
38 |
39 |
--------------------------------------------------------------------------------
/src/views/AdminVideoEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/AdminVideoList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Video List
4 | Add Video
5 |
6 |
7 |
Name
8 |
Description
9 |
Actions
10 |
11 |
12 |
{{ video.name }}
13 |
{{ video.description | abbreviate }}
14 |
15 | Watch
16 | Show
17 | Edit
18 | Delete
19 |
20 |
21 |
22 |
23 |
24 |
51 |
52 |
--------------------------------------------------------------------------------
/src/views/AdminVideoShow.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
{{video.name}}
4 |
5 |
6 |
14 |
15 |
16 |
17 |
18 |
60 |
61 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | All Videos
5 |
6 |
7 |
12 |
13 |
14 |
15 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/TagVideoList.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Videos with Tag "{{ tag.name }}"
5 |
6 |
7 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/views/UserLogin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Login
4 |
5 |
6 |
7 |
8 |
32 |
33 |
--------------------------------------------------------------------------------
/src/views/UserRegistration.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Register
4 |
5 |
6 |
7 |
8 |
27 |
28 |
--------------------------------------------------------------------------------
/src/views/VideoWatch.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | {{video.name}}
12 |
13 |
14 |
15 | Played
16 |
17 |
18 |
19 | Mark Played
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 | {{ getTag(tag_id) && getTag(tag_id).name }}
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
79 |
80 |
--------------------------------------------------------------------------------