This commit is contained in:
Richard Dern
2022-01-12 00:35:37 +01:00
commit 400e3d01f1
1363 changed files with 57778 additions and 0 deletions

View File

@@ -0,0 +1,235 @@
import { collect } from "collect.js";
/**
* Actions on documents
*/
export default {
/**
* Display a listing of the resource.
*/
async index(
{ commit, dispatch, getters },
{ documents, updateFeedItems = true }
) {
commit("setDocuments", documents);
let selectedDocuments = getters.selectedDocuments;
if (!selectedDocuments || selectedDocuments.length === 0) {
selectedDocuments = documents;
}
if (updateFeedItems) {
await dispatch("feedItems/index", selectedDocuments, {
root: true,
});
}
},
/**
* Store a newly created resource in storage.
*/
async store({ dispatch }, { url, folder_id, group_id }) {
const data = await api.post(route("document.store"), {
url: url,
folder_id: folder_id,
group_id: group_id,
});
dispatch("index", { documents: data });
},
/**
* Mark specified documents as selected.
*/
selectDocuments({ commit, dispatch }, { documents, selectFirstUnread }) {
commit("setSelectedDocuments", documents);
commit("feedItems/setSelectedFeedItems", [], {
root: true,
});
dispatch("feedItems/index", documents, { root: true }).then(
function () {
if (selectFirstUnread && documents) {
dispatch("feedItems/selectFirstUnreadFeedItem", null, {
root: true,
});
}
}
);
},
/**
* Select first document in currently displayed list
*/
selectFirstDocument({ getters, dispatch }) {
const document = collect(getters.documents).first();
dispatch("selectDocuments", [document]);
},
/**
* Select first document containing unread feed items in currently displayed
* list
*/
selectFirstDocumentWithUnreadItems(
{ getters, dispatch, rootGetters },
{ exclude, selectFirstUnread }
) {
if (!exclude) {
exclude = collect(getters.selectedDocuments).pluck("id").all();
}
const document = collect(getters.documents)
.where("feed_item_states_count", ">", 0)
.whereNotIn("id", exclude)
.first();
if (document) {
dispatch("selectDocuments", {
documents: [document],
selectFirstUnread: selectFirstUnread,
});
} else {
if (rootGetters["folders/selectedFolder"].type === "unread_items") {
dispatch("selectDocuments", {
selectFirstUnread: selectFirstUnread,
});
}
}
},
/**
* Remember documents being dragged
*/
startDraggingDocuments({ commit }, documents) {
commit("setDraggedDocuments", documents);
},
/**
* Forget dragged documents
*/
stopDraggingDocuments({ commit }) {
commit("setDraggedDocuments", []);
},
/**
* Move selected documents into specified folder
*/
async dropIntoFolder(
{ getters, commit, dispatch },
{ sourceFolder, targetFolder }
) {
const documents = collect(getters.draggedDocuments).pluck("id").all();
if (!documents || documents.length === 0) {
return;
}
const response = await api.post(
route("document.move", {
sourceFolder: sourceFolder.id,
targetFolder: targetFolder.id,
}),
{
documents: documents,
}
);
commit("setDraggedDocuments", []);
commit("setSelectedDocuments", []);
const newDocumentsList = collect(getters.documents).whereNotIn(
"id",
documents
);
dispatch("index", { documents: newDocumentsList });
dispatch("feedItems/index", getters.feeds, { root: true });
dispatch("feedItems/updateUnreadFeedItemsCount", response, {
root: true,
});
},
/**
* Increment visits for specified document
*/
async incrementVisits({ commit }, { document }) {
const data = await api.post(
route("document.visit", { document: document.id })
);
commit("update", {
document: document,
newProperties: data,
});
},
/**
* Open specified document in background
*/
openDocument({ dispatch }, { document }) {
window.open(document.url);
dispatch("incrementVisits", { document: document });
},
/**
* Remove specified documents from specified folder
*/
async destroy({ commit, getters, dispatch }, { folder, documents }) {
commit("setSelectedDocuments", []);
const data = await api.post(
route("document.destroy_bookmarks", { folder: folder.id }),
{
documents: collect(documents).pluck("id").all(),
}
);
dispatch("index", { documents: data });
},
/**
* Update the specified resource in storage.
*/
update({ commit, getters }, { documentId, newProperties }) {
const document = getters.documents.find((d) => d.id == documentId);
if (!document) {
return;
}
commit("update", { document: document, newProperties: newProperties });
},
/**
* Load every data available for specified document, unless it was already
* loaded
*/
load({ dispatch }, document) {
if (!document.loaded) {
return api
.get(route("document.show", document))
.then(function (response) {
response.loaded = true;
return dispatch("update", {
documentId: document.id,
newProperties: response,
});
});
}
},
followFeed({ commit }, feed) {
api.post(route("feed.follow", feed));
commit("ignoreFeed", { feed: feed, ignored: false });
},
ignoreFeed({ commit }, feed) {
api.post(route("feed.ignore", feed));
commit("ignoreFeed", { feed: feed, ignored: true });
},
};

View File

@@ -0,0 +1,29 @@
/**
* Documents getters
*/
export default {
/**
* Return documents list
*/
documents: state => {
return collect(state.documents).sortBy('title').all();
},
/**
* Return currently selected documents
*/
selectedDocuments: state => {
return state.selectedDocuments;
},
/**
* Return the first selected document
*/
selectedDocument: state => {
return collect(state.selectedDocuments).first();
},
/**
* Return documents being dragged
*/
draggedDocuments: state => {
return state.draggedDocuments;
}
}

12
resources/js/store/modules/documents/index.js vendored Executable file
View File

@@ -0,0 +1,12 @@
import state from "./state";
import getters from "./getters";
import actions from "./actions";
import mutations from "./mutations";
export default {
'namespaced': true,
state,
getters,
actions,
mutations
}

View File

@@ -0,0 +1,46 @@
/**
* Documents mutations
*/
export default {
/**
* Store documents list
* @param {*} state
* @param {*} documents
*/
setDocuments(state, documents) {
state.documents = documents;
},
/**
* Mark specified documents as selected
* @param {*} state
* @param {*} documents
*/
setSelectedDocuments(state, documents) {
state.selectedDocuments = documents;
},
/**
* Remember documents being dragged
* @param {*} state
* @param {*} documents
*/
setDraggedDocuments(state, documents) {
state.draggedDocuments = documents;
},
/**
* Update document's properties
* @param {*} state
* @param {*} param1
*/
update(state, {document, newProperties}) {
for(var property in newProperties) {
document[property] = newProperties[property];
}
},
ignoreFeed(state, {feed, ignored}) {
feed.is_ignored = ignored;
}
}

14
resources/js/store/modules/documents/state.js vendored Executable file
View File

@@ -0,0 +1,14 @@
export default {
/**
* Documents list
*/
documents: [],
/**
* Selected documents
*/
selectedDocuments: [],
/**
* Documents being dragged
*/
draggedDocuments: []
}

View File

@@ -0,0 +1,228 @@
import { collect } from "collect.js";
/**
* Feed items actions
*/
export default {
/**
* Load feed items for specified feeds
*/
async index({ getters, commit }, documents) {
const feeds = collect(documents)
.pluck("feeds")
.flatten(1)
.pluck("id")
.all();
let nextPage = 0;
let feedItems = [];
const currentlySelectedItem = getters.selectedFeedItem;
if (feeds.length > 0) {
const data = await api.get(route("feed_item.index"), {
feeds: feeds,
});
feedItems = data.data;
nextPage = data.next_page_url !== null ? data.current_page + 1 : 0;
}
if (currentlySelectedItem) {
const itemFeeds = collect(currentlySelectedItem.feeds)
.pluck("id")
.flatten(1);
if (itemFeeds.intersect(feeds)) {
let collection = collect(feedItems);
if (!collection.where("id", currentlySelectedItem.id).first()) {
feedItems.push(currentlySelectedItem);
}
}
}
commit("setNextPage", nextPage);
commit("setFeeds", feeds);
commit("setFeedItems", feedItems);
},
/**
* Infinite loading
*/
async loadMoreFeedItems({ getters, commit }) {
if (!getters.nextPage || !getters.feeds) {
return;
}
const items = getters.feedItems;
const data = await api.get(route("feed_item.index"), {
page: getters.nextPage,
feeds: getters.feeds,
});
const newItems = [...items, ...data.data];
commit(
"setNextPage",
data.next_page_url !== null ? data.current_page + 1 : 0
);
commit("setFeeds", getters.feeds);
commit("setFeedItems", newItems);
},
/**
* Change selected feed items
*/
async selectFeedItems({ commit }, feedItems) {
if (feedItems.length === 1) {
const data = await api.get(route("feed_item.show", feedItems[0]));
commit("update", { feedItem: feedItems[0], newProperties: data });
}
commit("setSelectedFeedItems", feedItems);
},
/**
* Select first unread item in current list
*/
selectFirstUnreadFeedItem({ getters, dispatch }, exclude) {
if (!exclude) {
exclude = [];
}
const nextFeedItem = collect(getters.feedItems)
.where("feed_item_states_count", ">", 0)
.whereNotIn("id", exclude)
.first();
if (nextFeedItem) {
dispatch("selectFeedItems", [nextFeedItem]);
} else {
dispatch(
"documents/selectFirstDocumentWithUnreadItems",
{ selectFirstUnread: true },
{
root: true,
}
);
}
},
selectNextUnreadFeedItem({ getters, dispatch }, currentId) {
if (!currentId) {
return dispatch("selectFirstUnreadFeedItem");
}
const nextFeedItem = collect(getters.feedItems)
.where("feed_item_states_count", ">", 0)
.skipUntil((item) => item.id === currentId)
.skip(1)
.shift();
if (nextFeedItem) {
dispatch("selectFeedItems", [nextFeedItem]);
} else {
dispatch(
"documents/selectFirstDocumentWithUnreadItems",
{ selectFirstUnread: true },
{
root: true,
}
);
}
},
/**
* Mark feed items as read
*/
markAsRead({ dispatch, commit, getters }, data) {
const nextPage = getters.nextPage;
// Avoid unwanted reloading
commit("setNextPage", null);
if ("feed_items" in data) {
dispatch("selectNextUnreadFeedItem", data.feed_items[0]);
} else if ("documents" in data) {
dispatch(
"documents/selectFirstDocumentWithUnreadItems",
{ exclude: data.documents },
{ root: true }
);
}
api.post(route("feed_item.mark_as_read"), data).then(function (
response
) {
dispatch("updateUnreadFeedItemsCount", response);
commit("setNextPage", nextPage);
});
},
/**
* Change number of unread feed items everywhere it's necessary
*/
updateUnreadFeedItemsCount({ commit, dispatch, getters }, data) {
if ("updated_feed_items" in data && data.updated_feed_items !== null) {
const feedItems = collect(getters.feedItems).whereIn(
"id",
data.updated_feed_items
);
feedItems.each(function (feedItem) {
commit("update", {
feedItem: feedItem,
newProperties: {
feed_item_states_count: 0,
},
});
});
}
if ("documents" in data) {
for (var documentId in data.documents) {
dispatch(
"documents/update",
{
documentId: documentId,
newProperties: {
feed_item_states_count: data.documents[documentId],
},
},
{ root: true }
);
}
}
if ("folders" in data) {
for (var folderId in data.folders) {
dispatch(
"folders/updateProperties",
{
folderId: folderId,
newProperties: {
feed_item_states_count: data.folders[folderId],
},
},
{ root: true }
);
}
}
if ("groups" in data) {
for (var groupId in data.groups) {
dispatch(
"groups/updateProperties",
{
groupId: groupId,
newProperties: {
feed_item_states_count: data.groups[groupId],
},
},
{ root: true }
);
}
}
},
};

View File

@@ -0,0 +1,48 @@
export default {
/**
* Feed items present in current selection (folder, document, feed)
*/
feedItems: state => {
return state.feedItems;
},
/**
* Return first feed item in current list
*/
feedItem: state => {
return collect(state.feedItems).first();
},
/**
* Return currently selected feed items
*/
selectedFeedItems: state => {
if(!state.selectedFeedItems) {
return [];
}
return state.selectedFeedItems;
},
/**
* Return the first selected feed item
*/
selectedFeedItem: state => {
return collect(state.selectedFeedItems).first();
},
/**
* Return next page #
*/
nextPage: state => {
return state.nextPage;
},
/**
* Return feeds associated with current feed items
*/
feeds: state => {
return state.feeds;
},
/**
* Return a boolean value indicating if we can load more feed items
*/
canLoadMore: state => {
return state.nextPage > 1;
}
};

12
resources/js/store/modules/feeditems/index.js vendored Executable file
View File

@@ -0,0 +1,12 @@
import state from "./state";
import getters from "./getters";
import actions from "./actions";
import mutations from "./mutations";
export default {
'namespaced': true,
state,
getters,
actions,
mutations
}

View File

@@ -0,0 +1,52 @@
export default {
/**
* Set feed items list
* @param {*} state
* @param {*} feedItems
*/
setFeedItems(state, feedItems) {
state.feedItems = feedItems;
},
/**
* Set next page #
* @param {*} state
* @param {*} page
*/
setNextPage(state, page) {
state.nextPage = page;
},
/**
* Set feeds associated to current feed items
* @param {*} state
* @param {*} feeds
*/
setFeeds(state, feeds) {
state.feeds = feeds;
},
/**
* Set selected feed items
* @param {*} state
* @param {*} feedItems
*/
setSelectedFeedItems(state, feedItems) {
if(!feedItems) {
feedItems = [];
}
state.selectedFeedItems = feedItems;
},
/**
* Update feed item's properties
* @param {*} state
* @param {*} param1
*/
update(state, {feedItem, newProperties}) {
for(var property in newProperties) {
feedItem[property] = newProperties[property];
}
},
};

18
resources/js/store/modules/feeditems/state.js vendored Executable file
View File

@@ -0,0 +1,18 @@
export default {
/**
* Feed items present in current selection (folder, document, feed)
*/
feedItems: [],
/**
* Next page #
*/
nextPage: 0,
/**
* Feeds associated with current feed items
*/
feeds: [],
/**
* Selected feed items
*/
selectedFeedItems: [],
};

231
resources/js/store/modules/folders/actions.js vendored Executable file
View File

@@ -0,0 +1,231 @@
/**
* Actions on folders
*/
export default {
/*
* -------------------------------------------------------------------------
* ----| CRUD |-------------------------------------------------------------
* -------------------------------------------------------------------------
*/
/**
* Display a listing of the resource.
*/
async index({ getters, commit, dispatch }, { folders, show }) {
commit("setFolders", folders);
if (show) {
if (show === "unread_items") {
show = getters.getUnreadItemsFolder;
}
dispatch("show", { folder: show });
} else {
dispatch("show", {});
}
},
/**
* Display the specified resource.
*/
show(
{ commit, getters, dispatch },
{ folder, deselectDocuments = true, updateFeedItems = true }
) {
const currentSelectedFolder = getters.selectedFolder;
if (!folder) {
folder = currentSelectedFolder;
} else if (Number.isInteger(folder)) {
folder = getters.folders.find((f) => f.id === folder);
}
commit("setSelectedFolder", folder);
if (deselectDocuments) {
dispatch("documents/selectDocuments", [], { root: true });
}
api.get(route("folder.show", folder)).then(function (response) {
dispatch(
"documents/index",
{ documents: response, updateFeedItems: updateFeedItems },
{ root: true }
);
});
},
/**
* Load folder's details
*/
async loadDetails({ dispatch }, folder) {
if (!folder.details_loaded && !folder.details_loading) {
dispatch("updateProperties", {
folderId: folder.id,
newProperties: {
details_loading: true,
},
});
let response = await api.get(route("folder.details", folder));
response.details_loaded = true;
response.details_loading = false;
dispatch("updateProperties", {
folderId: folder.id,
newProperties: response,
});
}
},
/**
* Store a newly created resource in storage.
*/
store({ dispatch }, { title, parent_id, group_id }) {
return api
.post(route("folder.store"), {
title,
parent_id,
group_id,
})
.then((data) => {
dispatch("index", { folders: data });
})
.catch((error) => console.error(error));
},
/**
* Update the specified resource in storage.
*/
async update({ dispatch }, { folder, newProperties }) {
const data = await api.put(
route("folder.update", folder),
newProperties
);
dispatch("updateProperties", {
folderId: folder.id,
newProperties: data,
});
},
/**
* Update the specified resource in storage.
*/
updateProperties({ commit, getters }, { folderId, newProperties }) {
const folder = getters.folders.find((f) => f.id == folderId);
if (!folder) {
return;
}
commit("update", { folder: folder, newProperties: newProperties });
},
updatePermission({ dispatch }, { folder, user, ability, granted }) {
api.post(route("folder.set_permission", folder), {
ability: ability,
granted: granted,
user_id: user,
}).then(function (response) {
dispatch("updateProperties", {
folderId: folder.id,
newProperties: response,
});
});
},
/**
* Remove the specified resource from storage.
*/
async destroy({ dispatch }, folder) {
const data = await api.delete(route("folder.destroy", folder));
dispatch("index", { folders: data });
},
/**
* Toggle specified folder expanded/collapsed
*/
toggleExpanded({ dispatch }, folder) {
dispatch("update", {
folder: folder,
newProperties: {
...folder,
...{
is_expanded: !folder.is_expanded,
},
},
});
},
/**
* Toggle specified folder's branch expanded/collapsed
*/
async toggleBranch({ commit }, folder) {
const response = await api.post(route("folder.toggle_branch", folder));
commit("setFolders", response);
},
/*
* -------------------------------------------------------------------------
* ----| Drag'n'drop |------------------------------------------------------
* -------------------------------------------------------------------------
*/
/**
* Remind folder being dragged
*/
startDraggingFolder({ commit }, folder) {
commit("setDraggedFolder", folder);
},
/**
* Forget folder being dragged
*/
stopDraggingFolder({ commit }) {
commit("setDraggedFolder", null);
},
/**
* Drop something into specified folder
*/
async dropIntoFolder({ getters, dispatch }, folder) {
var sourceFolder = getters.draggedFolder;
if (!sourceFolder) {
sourceFolder = getters.selectedFolder;
await dispatch(
"documents/dropIntoFolder",
{ sourceFolder: sourceFolder, targetFolder: folder },
{ root: true }
);
return;
}
if (
sourceFolder.parent_id === folder.id ||
sourceFolder.id === folder.id
) {
return;
}
const newProperties = {
parent_id: folder.id,
};
await dispatch("update", {
folder: sourceFolder,
newProperties: {
...sourceFolder,
...newProperties,
},
}).then(function () {
dispatch("groups/show", {}, { root: true });
});
},
};

37
resources/js/store/modules/folders/getters.js vendored Executable file
View File

@@ -0,0 +1,37 @@
/**
* Folders getters
*/
export default {
/**
* Return folders tree
*/
folders: state => {
return state.folders;
},
/**
* Return currently selected folder
*/
selectedFolder: state => {
var folders = state.folders ? state.folders : [];
return folders.find(folder => folder.is_selected);
},
/**
* Return folder being dragged
*/
draggedFolder: state => {
return state.draggedFolder;
},
/**
* Return a folder by its id
*/
getFolderById: (state) => (id) => {
return state.folders.find(folder => folder.id === id);
},
/**
* Return the "unread items" folder
*/
getUnreadItemsFolder: (state) => {
return state.folders.find(folder => folder.type === 'unread_items');
}
}

12
resources/js/store/modules/folders/index.js vendored Executable file
View File

@@ -0,0 +1,12 @@
import state from "./state";
import getters from "./getters";
import actions from "./actions";
import mutations from "./mutations";
export default {
'namespaced': true,
state,
getters,
actions,
mutations
}

View File

@@ -0,0 +1,52 @@
/**
* Folders mutations
*/
export default {
/**
* Store folders list
* @param {*} state
* @param {*} folders
*/
setFolders(state, folders) {
state.folders = folders;
},
/**
* Unselect all folders, and mark specified folder as selected
* @param {*} state
* @param {*} folder
*/
setSelectedFolder(state, folder) {
state.folders.find((f) => (f.is_selected = false));
folder.is_selected = true;
},
/**
* Update folder's properties
* @param {*} state
* @param {*} param1
*/
update(state, { folder, newProperties }) {
for (var property in newProperties) {
folder[property] = newProperties[property];
}
if (folder.type === "unread_items") {
if (folder.feed_item_states_count) {
folder.iconColor = "folder-unread-not-empty";
} else {
folder.iconColor = "folder-unread";
}
}
},
/**
* Store folder being dragged during a drag'n'drop process
* @param {*} state
* @param {*} folder
*/
setDraggedFolder(state, folder) {
state.draggedFolder = folder;
},
};

10
resources/js/store/modules/folders/state.js vendored Executable file
View File

@@ -0,0 +1,10 @@
export default {
/**
* Folders tree
*/
folders: [],
/**
* Folder being dragged
*/
draggedFolder: null
}

137
resources/js/store/modules/groups/actions.js vendored Executable file
View File

@@ -0,0 +1,137 @@
/**
* Actions on groups
*/
export default {
/*
* -------------------------------------------------------------------------
* ----| CRUD |-------------------------------------------------------------
* -------------------------------------------------------------------------
*/
/**
* Display a listing of the resource - Groups in which user is active
*/
async indexActive({ commit }) {
const data = await api.get(route("group.index_active"));
commit("setGroups", data);
},
/**
* Display a listing of the resource - Groups associated with this user
*/
async indexMyGroups({ commit }) {
const data = await api.get(route("group.my_groups"));
commit("setGroups", data);
},
/**
* Display the specified resource.
*/
show({ getters, dispatch, rootGetters }, { group, folder }) {
const currentSelectedGroup = getters.selectedGroup;
if (!group) {
group = currentSelectedGroup;
} else if (Number.isInteger(group)) {
group = getters.groups.find((g) => g.id === group);
}
dispatch("selectGroup", group);
dispatch("documents/selectDocuments", [], { root: true });
api.get(route("group.show", group)).then(function (folders) {
if (folder) {
dispatch(
"folders/index",
{ folders: folders, show: folder },
{ root: true }
);
} else {
dispatch("folders/index", { folders: folders }, { root: true });
}
});
},
/**
* Mark specified group as selected
*/
selectGroup({ commit, getters }, group) {
if (Number.isInteger(group)) {
group = getters.groups.find((g) => g.id === group);
}
commit("setSelectedGroup", group);
},
/**
* Update a group
*/
updateGroup({ dispatch }, { group, newProperties }) {
api.put(route("group.update", group), newProperties).then(function (
response
) {
dispatch("updateProperties", {
groupId: group.id,
newProperties: response,
});
});
},
/**
* Delete group
*/
deleteGroup({ commit }, group) {
api.delete(route("group.destroy", group)).then(function (response) {
commit("setGroups", response);
});
},
/**
* Update the specified resource in storage.
*/
updateProperties({ commit, getters }, { groupId, newProperties }) {
const group = getters.groups.find((g) => g.id == groupId);
if (!group) {
console.warn("Group #" + groupId + " not found");
return;
}
commit("update", { group: group, newProperties: newProperties });
},
/**
* Create a group
*/
createGroup({ commit }, properties) {
api.post(route("group.store"), properties).then(function (response) {
commit("setGroups", response);
});
},
/**
* Update my groups positions
*/
updatePositions({ getters, commit }, { positions }) {
for (var groupId in positions) {
const group = getters.groups.find((g) => g.id == groupId);
if (!group) {
console.warn("Group #" + groupId + " not found");
return;
}
commit("updatePosition", {
group: group,
position: positions[groupId],
});
}
api.post(route("group.update_positions"), {
positions: positions,
});
},
};

19
resources/js/store/modules/groups/getters.js vendored Executable file
View File

@@ -0,0 +1,19 @@
/**
* Groups getters
*/
export default {
/**
* Return groups
*/
groups: state => {
return state.groups;
},
/**
* Return currently selected group
*/
selectedGroup: state => {
var groups = state.groups ? state.groups : [];
return groups.find(group => group.is_selected);
}
}

12
resources/js/store/modules/groups/index.js vendored Executable file
View File

@@ -0,0 +1,12 @@
import state from "./state";
import getters from "./getters";
import actions from "./actions";
import mutations from "./mutations";
export default {
'namespaced': true,
state,
getters,
actions,
mutations
}

View File

@@ -0,0 +1,39 @@
/**
* Groups mutations
*/
export default {
/**
* Store groups list
* @param {*} state
* @param {*} groups
*/
setGroups(state, groups) {
state.groups = groups;
},
/**
* Unselect all groups, and mark specified group as selected
* @param {*} state
* @param {*} group
*/
setSelectedGroup(state, group) {
state.groups.find(g => (g.is_selected = false));
group.is_selected = true;
},
/**
* Update group's properties
* @param {*} state
* @param {*} param1
*/
update(state, { group, newProperties }) {
for (var property in newProperties) {
group[property] = newProperties[property];
}
},
updatePosition(state, { group, position }) {
group.pivot.position = position;
}
};

6
resources/js/store/modules/groups/state.js vendored Executable file
View File

@@ -0,0 +1,6 @@
export default {
/**
* Groups
*/
groups: []
}