mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
Merge branch 'master' into 2019/kw22/refactor_backend_structure
# Conflicts: # backend/src/middleware/index.js # backend/src/schema/resolvers/comments.spec.js # backend/src/schema/resolvers/socialMedia.spec.js
This commit is contained in:
commit
6948666cfe
@ -47,7 +47,7 @@
|
||||
"apollo-client": "~2.5.1",
|
||||
"apollo-link-context": "~1.0.14",
|
||||
"apollo-link-http": "~1.5.14",
|
||||
"apollo-server": "~2.5.1",
|
||||
"apollo-server": "~2.6.1",
|
||||
"bcryptjs": "~2.4.3",
|
||||
"cheerio": "~1.0.0-rc.3",
|
||||
"cors": "~2.8.5",
|
||||
@ -88,7 +88,7 @@
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.2.0",
|
||||
"@babel/preset-env": "~7.4.5",
|
||||
"@babel/register": "~7.4.4",
|
||||
"apollo-server-testing": "~2.5.1",
|
||||
"apollo-server-testing": "~2.6.1",
|
||||
"babel-core": "~7.0.0-0",
|
||||
"babel-eslint": "~10.0.1",
|
||||
"babel-jest": "~24.8.0",
|
||||
|
||||
@ -1,42 +1,63 @@
|
||||
import activityPubMiddleware from './activityPubMiddleware'
|
||||
import passwordMiddleware from './passwordMiddleware'
|
||||
import softDeleteMiddleware from './softDeleteMiddleware'
|
||||
import sluggifyMiddleware from './sluggifyMiddleware'
|
||||
import fixImageUrlsMiddleware from './fixImageUrlsMiddleware'
|
||||
import excerptMiddleware from './excerptMiddleware'
|
||||
import dateTimeMiddleware from './dateTimeMiddleware'
|
||||
import xssMiddleware from './xssMiddleware'
|
||||
import permissionsMiddleware from './permissionsMiddleware'
|
||||
import userMiddleware from './userMiddleware'
|
||||
import includedFieldsMiddleware from './includedFieldsMiddleware'
|
||||
import orderByMiddleware from './orderByMiddleware'
|
||||
import validationMiddleware from './validation'
|
||||
import notificationsMiddleware from './notifications'
|
||||
import CONFIG from './../config'
|
||||
import activityPub from './activityPubMiddleware'
|
||||
import password from './passwordMiddleware'
|
||||
import softDelete from './softDeleteMiddleware'
|
||||
import sluggify from './sluggifyMiddleware'
|
||||
import fixImageUrls from './fixImageUrlsMiddleware'
|
||||
import excerpt from './excerptMiddleware'
|
||||
import dateTime from './dateTimeMiddleware'
|
||||
import xss from './xssMiddleware'
|
||||
import permissions from './permissionsMiddleware'
|
||||
import user from './userMiddleware'
|
||||
import includedFields from './includedFieldsMiddleware'
|
||||
import orderBy from './orderByMiddleware'
|
||||
import validation from './validation'
|
||||
import notifications from './notifications'
|
||||
|
||||
export default schema => {
|
||||
let middleware = [
|
||||
passwordMiddleware,
|
||||
dateTimeMiddleware,
|
||||
validationMiddleware,
|
||||
sluggifyMiddleware,
|
||||
excerptMiddleware,
|
||||
notificationsMiddleware,
|
||||
xssMiddleware,
|
||||
fixImageUrlsMiddleware,
|
||||
softDeleteMiddleware,
|
||||
userMiddleware,
|
||||
includedFieldsMiddleware,
|
||||
orderByMiddleware,
|
||||
const middlewares = {
|
||||
permissions: permissions,
|
||||
activityPub: activityPub,
|
||||
password: password,
|
||||
dateTime: dateTime,
|
||||
validation: validation,
|
||||
sluggify: sluggify,
|
||||
excerpt: excerpt,
|
||||
notifications: notifications,
|
||||
xss: xss,
|
||||
fixImageUrls: fixImageUrls,
|
||||
softDelete: softDelete,
|
||||
user: user,
|
||||
includedFields: includedFields,
|
||||
orderBy: orderBy,
|
||||
}
|
||||
|
||||
let order = [
|
||||
'permissions',
|
||||
'activityPub',
|
||||
'password',
|
||||
'dateTime',
|
||||
'validation',
|
||||
'sluggify',
|
||||
'excerpt',
|
||||
'notifications',
|
||||
'xss',
|
||||
'fixImageUrls',
|
||||
'softDelete',
|
||||
'user',
|
||||
'includedFields',
|
||||
'orderBy',
|
||||
]
|
||||
|
||||
// add permisions middleware at the first position (unless we're seeding)
|
||||
// NOTE: DO NOT SET THE PERMISSION FLAT YOUR SELF
|
||||
if (CONFIG.DEBUG) {
|
||||
const disabled = CONFIG.DISABLED_MIDDLEWARES.split(',')
|
||||
if (!disabled.includes('activityPub')) middleware.unshift(activityPubMiddleware)
|
||||
if (!disabled.includes('permissions'))
|
||||
middleware.unshift(permissionsMiddleware.generate(schema))
|
||||
const disabledMiddlewares = CONFIG.DISABLED_MIDDLEWARES.split(',')
|
||||
order = order.filter(key => {
|
||||
return !disabledMiddlewares.includes(key)
|
||||
})
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log(`Warning: "${disabledMiddlewares}" middlewares have been disabled.`)
|
||||
}
|
||||
return middleware
|
||||
|
||||
return order.map(key => middlewares[key])
|
||||
}
|
||||
|
||||
@ -16,11 +16,15 @@ const isAdmin = rule()(async (parent, args, { user }, info) => {
|
||||
return user && user.role === 'admin'
|
||||
})
|
||||
|
||||
const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info) => {
|
||||
const isMyOwn = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (parent, args, context, info) => {
|
||||
return context.user.id === parent.id
|
||||
})
|
||||
|
||||
const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
||||
const belongsToMe = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (_, args, context) => {
|
||||
const {
|
||||
driver,
|
||||
user: { id: userId },
|
||||
@ -32,7 +36,10 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
||||
MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId})
|
||||
RETURN n
|
||||
`,
|
||||
{ userId, notificationId },
|
||||
{
|
||||
userId,
|
||||
notificationId,
|
||||
},
|
||||
)
|
||||
const [notification] = result.records.map(record => {
|
||||
return record.get('n')
|
||||
@ -41,12 +48,16 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => {
|
||||
return Boolean(notification)
|
||||
})
|
||||
|
||||
const onlyEnabledContent = rule({ cache: 'strict' })(async (parent, args, ctx, info) => {
|
||||
const onlyEnabledContent = rule({
|
||||
cache: 'strict',
|
||||
})(async (parent, args, ctx, info) => {
|
||||
const { disabled, deleted } = args
|
||||
return !(disabled || deleted)
|
||||
})
|
||||
|
||||
const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver }) => {
|
||||
const isAuthor = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (parent, args, { user, driver }) => {
|
||||
if (!user) return false
|
||||
const session = driver.session()
|
||||
const { id: postId } = args
|
||||
@ -55,7 +66,9 @@ const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver
|
||||
MATCH (post:Post {id: $postId})<-[:WROTE]-(author)
|
||||
RETURN author
|
||||
`,
|
||||
{ postId },
|
||||
{
|
||||
postId,
|
||||
},
|
||||
)
|
||||
const [author] = result.records.map(record => {
|
||||
return record.get('author')
|
||||
@ -100,6 +113,7 @@ const permissions = shield({
|
||||
enable: isModerator,
|
||||
disable: isModerator,
|
||||
CreateComment: isAuthenticated,
|
||||
DeleteComment: isAuthenticated,
|
||||
// CreateUser: allow,
|
||||
},
|
||||
User: {
|
||||
|
||||
@ -53,6 +53,11 @@ export default {
|
||||
)
|
||||
session.close()
|
||||
|
||||
return comment
|
||||
},
|
||||
DeleteComment: async (object, params, context, resolveInfo) => {
|
||||
const comment = await neo4jgraphql(object, params, context, resolveInfo, false)
|
||||
|
||||
return comment
|
||||
},
|
||||
},
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import gql from 'graphql-tag'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login } from '../../jest/helpers'
|
||||
@ -5,6 +6,7 @@ import { host, login } from '../../jest/helpers'
|
||||
const factory = Factory()
|
||||
let client
|
||||
let createCommentVariables
|
||||
let deleteCommentVariables
|
||||
let createPostVariables
|
||||
let createCommentVariablesSansPostId
|
||||
let createCommentVariablesWithNonExistentPost
|
||||
@ -21,22 +23,22 @@ afterEach(async () => {
|
||||
})
|
||||
|
||||
describe('CreateComment', () => {
|
||||
const createCommentMutation = `
|
||||
mutation($postId: ID, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
const createCommentMutation = gql`
|
||||
mutation($postId: ID, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const createPostMutation = `
|
||||
mutation($id: ID!, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
const createPostMutation = gql`
|
||||
mutation($id: ID!, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const commentQueryForPostId = `
|
||||
const commentQueryForPostId = gql`
|
||||
query($content: String) {
|
||||
Comment(content: $content) {
|
||||
postId
|
||||
@ -59,8 +61,13 @@ describe('CreateComment', () => {
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
headers = await login({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
createCommentVariables = {
|
||||
postId: 'p1',
|
||||
content: "I'm authorised to comment",
|
||||
@ -88,15 +95,25 @@ describe('CreateComment', () => {
|
||||
it('assigns the authenticated user as author', async () => {
|
||||
await client.request(createCommentMutation, createCommentVariables)
|
||||
|
||||
const { User } = await client.request(`{
|
||||
const { User } = await client.request(gql`
|
||||
{
|
||||
User(email: "test@example.org") {
|
||||
comments {
|
||||
content
|
||||
}
|
||||
}
|
||||
}`)
|
||||
}
|
||||
`)
|
||||
|
||||
expect(User).toEqual([{ comments: [{ content: "I'm authorised to comment" }] }])
|
||||
expect(User).toEqual([
|
||||
{
|
||||
comments: [
|
||||
{
|
||||
content: "I'm authorised to comment",
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
it('throw an error if an empty string is sent from the editor as content', async () => {
|
||||
@ -186,7 +203,92 @@ describe('CreateComment', () => {
|
||||
commentQueryForPostId,
|
||||
commentQueryVariablesByContent,
|
||||
)
|
||||
expect(Comment).toEqual([{ postId: null }])
|
||||
expect(Comment).toEqual([
|
||||
{
|
||||
postId: null,
|
||||
},
|
||||
])
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteComment', () => {
|
||||
const createCommentMutation = gql`
|
||||
mutation($postId: ID, $content: String!) {
|
||||
CreateComment(postId: $postId, content: $content) {
|
||||
id
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
const deleteCommentMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeleteComment(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
const createPostMutation = gql`
|
||||
mutation($id: ID!, $title: String!, $content: String!) {
|
||||
CreatePost(id: $id, title: $title, content: $content) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
deleteCommentVariables = {
|
||||
id: 'c1',
|
||||
}
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow(
|
||||
'Not Authorised',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
let headers
|
||||
beforeEach(async () => {
|
||||
headers = await login({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
createCommentVariables = {
|
||||
id: 'c1',
|
||||
postId: 'p1',
|
||||
content: "I'm authorised to comment",
|
||||
}
|
||||
deleteCommentVariables = {
|
||||
id: 'c1',
|
||||
}
|
||||
createPostVariables = {
|
||||
id: 'p1',
|
||||
title: 'post to comment on',
|
||||
content: 'please comment on me',
|
||||
}
|
||||
await client.request(createPostMutation, createPostVariables)
|
||||
})
|
||||
|
||||
it('deletes the authors comment', async () => {
|
||||
const { CreateComment } = await client.request(createCommentMutation, createCommentVariables)
|
||||
|
||||
deleteCommentVariables = {
|
||||
id: CreateComment.id,
|
||||
}
|
||||
const expected = {
|
||||
DeleteComment: {
|
||||
id: CreateComment.id,
|
||||
},
|
||||
}
|
||||
await expect(
|
||||
client.request(deleteCommentMutation, deleteCommentVariables),
|
||||
).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it.todo('throws an error if it tries to delete a comment not from this author')
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,13 +1,15 @@
|
||||
import gql from 'graphql-tag'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login } from '../../jest/helpers'
|
||||
|
||||
|
||||
const factory = Factory()
|
||||
|
||||
describe('CreateSocialMedia', () => {
|
||||
describe('SocialMedia', () => {
|
||||
let client
|
||||
let headers
|
||||
const mutationC = `
|
||||
const mutationC = gql`
|
||||
mutation($url: String!) {
|
||||
CreateSocialMedia(url: $url) {
|
||||
id
|
||||
@ -15,7 +17,7 @@ describe('CreateSocialMedia', () => {
|
||||
}
|
||||
}
|
||||
`
|
||||
const mutationD = `
|
||||
const mutationD = gql`
|
||||
mutation($id: ID!) {
|
||||
DeleteSocialMedia(id: $id) {
|
||||
id
|
||||
@ -42,19 +44,28 @@ describe('CreateSocialMedia', () => {
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
client = new GraphQLClient(host)
|
||||
const variables = { url: 'http://nsosp.org' }
|
||||
const variables = {
|
||||
url: 'http://nsosp.org',
|
||||
}
|
||||
await expect(client.request(mutationC, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
headers = await login({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
})
|
||||
client = new GraphQLClient(host, {
|
||||
headers,
|
||||
})
|
||||
})
|
||||
|
||||
it('creates social media with correct URL', async () => {
|
||||
const variables = { url: 'http://nsosp.org' }
|
||||
const variables = {
|
||||
url: 'http://nsosp.org',
|
||||
}
|
||||
await expect(client.request(mutationC, variables)).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
CreateSocialMedia: {
|
||||
@ -66,11 +77,15 @@ describe('CreateSocialMedia', () => {
|
||||
})
|
||||
|
||||
it('deletes social media', async () => {
|
||||
const creationVariables = { url: 'http://nsosp.org' }
|
||||
const creationVariables = {
|
||||
url: 'http://nsosp.org',
|
||||
}
|
||||
const { CreateSocialMedia } = await client.request(mutationC, creationVariables)
|
||||
const { id } = CreateSocialMedia
|
||||
|
||||
const deletionVariables = { id }
|
||||
const deletionVariables = {
|
||||
id,
|
||||
}
|
||||
const expected = {
|
||||
DeleteSocialMedia: {
|
||||
id: id,
|
||||
@ -81,12 +96,16 @@ describe('CreateSocialMedia', () => {
|
||||
})
|
||||
|
||||
it('rejects empty string', async () => {
|
||||
const variables = { url: '' }
|
||||
const variables = {
|
||||
url: '',
|
||||
}
|
||||
await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL')
|
||||
})
|
||||
|
||||
it('validates URLs', async () => {
|
||||
const variables = { url: 'not-a-url' }
|
||||
const variables = {
|
||||
url: 'not-a-url',
|
||||
}
|
||||
await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL')
|
||||
})
|
||||
})
|
||||
|
||||
@ -9,13 +9,6 @@
|
||||
dependencies:
|
||||
apollo-env "0.5.1"
|
||||
|
||||
"@apollographql/apollo-tools@^0.3.6-alpha.1":
|
||||
version "0.3.6-alpha.1"
|
||||
resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.6-alpha.1.tgz#5199b36c65c2fddc4f8bc8bb97642f74e9fb00c5"
|
||||
integrity sha512-fq74In3Vw9OmtKHze0L5/Ns/pdTZOqUeFVC6Um9NRgziVehXz/qswsh2r3+dsn82uqoa/AlvckHtd6aPPPYj9g==
|
||||
dependencies:
|
||||
apollo-env "0.4.1-alpha.1"
|
||||
|
||||
"@apollographql/graphql-playground-html@1.6.20":
|
||||
version "1.6.20"
|
||||
resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz#bf9f2acdf319c0959fad8ec1239741dd2ead4e8d"
|
||||
@ -1288,13 +1281,13 @@ anymatch@^2.0.0:
|
||||
micromatch "^3.1.4"
|
||||
normalize-path "^2.1.1"
|
||||
|
||||
apollo-cache-control@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.6.1.tgz#c73ff521fe606faf18edcbd3463c421a966f3e5d"
|
||||
integrity sha512-M3cDeQDXtRxYPQ/sL4pu3IVE5Q/9jpBlENB2IjwxTDir+WFZbJV1CAnvVwyJdL1DvS6ESR35DFOurJH4Ws/OPA==
|
||||
apollo-cache-control@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.1.tgz#3d4fba232f561f096f61051e103bf58ee4bf8b54"
|
||||
integrity sha512-3h1TEoMnzex6IIiFb5Ja3owTyLwT5YzK69cRgrSpSscdpYc3ID4KVs0Ht9cbOUmb/L/UKtYVkRC8KeVAYmHEjQ==
|
||||
dependencies:
|
||||
apollo-server-env "2.3.0"
|
||||
graphql-extensions "0.6.1"
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.7.1"
|
||||
|
||||
apollo-cache-control@^0.1.0:
|
||||
version "0.1.1"
|
||||
@ -1345,13 +1338,13 @@ apollo-client@~2.5.1:
|
||||
tslib "^1.9.3"
|
||||
zen-observable "^0.8.0"
|
||||
|
||||
apollo-datasource@0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.4.0.tgz#f042641fd2593fa5f4f002fc30d1fb1a20284df8"
|
||||
integrity sha512-6QkgnLYwQrW0qv+yXIf617DojJbGmza2XJXUlgnzrGGhxzfAynzEjaLyYkc8rYS1m82vjrl9EOmLHTcnVkvZAQ==
|
||||
apollo-datasource@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.5.0.tgz#7a8c97e23da7b9c15cb65103d63178ab19eca5e9"
|
||||
integrity sha512-SVXxJyKlWguuDjxkY/WGlC/ykdsTmPxSF0z8FenagcQ91aPURXzXP1ZDz5PbamY+0iiCRubazkxtTQw4GWTFPg==
|
||||
dependencies:
|
||||
apollo-server-caching "0.4.0"
|
||||
apollo-server-env "2.3.0"
|
||||
apollo-server-env "2.4.0"
|
||||
|
||||
apollo-engine-reporting-protobuf@0.3.0:
|
||||
version "0.3.0"
|
||||
@ -1360,26 +1353,17 @@ apollo-engine-reporting-protobuf@0.3.0:
|
||||
dependencies:
|
||||
protobufjs "^6.8.6"
|
||||
|
||||
apollo-engine-reporting@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.1.1.tgz#f5a3240bc5c5afb210ff8c45d72995de7b0d2a13"
|
||||
integrity sha512-K7BDsj99jr8ftd9NIuHL4oF/S7CBFcgMGjL0ChhfxpkgUv80FPxJ+9Fs+9ZkKIVylV3PCi2WnihpDeEO10eZAw==
|
||||
apollo-engine-reporting@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.2.1.tgz#0b77fad2e9221d62f4a29b8b4fab8f7f47dcc1d6"
|
||||
integrity sha512-DVXZhz/nSZR4lphakjb1guAD0qJ7Wm1PVtZEBjN097cnOor4XSOzQlPfTaYtVuhlxUKUuCx1syiBbOuV8sKqXg==
|
||||
dependencies:
|
||||
apollo-engine-reporting-protobuf "0.3.0"
|
||||
apollo-graphql "^0.2.1-alpha.1"
|
||||
apollo-server-core "2.5.1"
|
||||
apollo-server-env "2.3.0"
|
||||
apollo-graphql "^0.3.0"
|
||||
apollo-server-core "2.6.1"
|
||||
apollo-server-env "2.4.0"
|
||||
async-retry "^1.2.1"
|
||||
graphql-extensions "0.6.1"
|
||||
|
||||
apollo-env@0.4.1-alpha.1:
|
||||
version "0.4.1-alpha.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.4.1-alpha.1.tgz#10d3ea508b8f3ba03939ef4e6ec4b2b5db77e8f1"
|
||||
integrity sha512-4qWiaUKWh92jvKxxRsiZSjmW9YH9GWSG1W6X+S1BcC1uqtPiHsem7ExG9MMTt+UrzHsbpQLctj12xk8lI4lgCg==
|
||||
dependencies:
|
||||
core-js "3.0.0-beta.13"
|
||||
node-fetch "^2.2.0"
|
||||
sha.js "^2.4.11"
|
||||
graphql-extensions "0.7.1"
|
||||
|
||||
apollo-env@0.5.1:
|
||||
version "0.5.1"
|
||||
@ -1398,12 +1382,12 @@ apollo-errors@^1.9.0:
|
||||
assert "^1.4.1"
|
||||
extendable-error "^0.1.5"
|
||||
|
||||
apollo-graphql@^0.2.1-alpha.1:
|
||||
version "0.2.1-alpha.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.2.1-alpha.1.tgz#a0cc0bd65e03c7e887c96c9f53421f3c6dd7b599"
|
||||
integrity sha512-kObCSpYRHEf4IozJV+TZAXEL2Yni2DpzQckohJNYXg5/KRAF20jJ7lHxuJz+kMQrc7QO4wYGSa29HuFZH2AtQA==
|
||||
apollo-graphql@^0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.3.1.tgz#d13b80cc0cae3fe7066b81b80914c6f983fac8d7"
|
||||
integrity sha512-tbhtzNAAhNI34v4XY9OlZGnH7U0sX4BP1cJrUfSiNzQnZRg1UbQYZ06riHSOHpi5RSndFcA9LDM5C1ZKKOUeBg==
|
||||
dependencies:
|
||||
apollo-env "0.4.1-alpha.1"
|
||||
apollo-env "0.5.1"
|
||||
lodash.sortby "^4.7.0"
|
||||
|
||||
apollo-link-context@~1.0.14:
|
||||
@ -1456,24 +1440,24 @@ apollo-server-caching@0.4.0:
|
||||
dependencies:
|
||||
lru-cache "^5.0.0"
|
||||
|
||||
apollo-server-core@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.5.1.tgz#0fdb6cfca56a0f5b5b3aecffb48db17b3c8e1d71"
|
||||
integrity sha512-4QNrW1AUM3M/p0+hbBX/MsjSjZTy+2rt7JpiKKkG9RmeEIzd/VG7hwwwloAZSLjYx3twz0+BnASJ9y+rGEPC8A==
|
||||
apollo-server-core@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.1.tgz#d0d878b0a4959b6c661fc43300ce45b29996176a"
|
||||
integrity sha512-jO2BtcP7ozSSK5qtw1gGDwO66WSNtzhvpDJD7erkA9byv8Z0jB2QIPNWN6iaj311LaPahM05k+8hMIhFy9oHWg==
|
||||
dependencies:
|
||||
"@apollographql/apollo-tools" "^0.3.6"
|
||||
"@apollographql/graphql-playground-html" "1.6.20"
|
||||
"@types/ws" "^6.0.0"
|
||||
apollo-cache-control "0.6.1"
|
||||
apollo-datasource "0.4.0"
|
||||
apollo-engine-reporting "1.1.1"
|
||||
apollo-cache-control "0.7.1"
|
||||
apollo-datasource "0.5.0"
|
||||
apollo-engine-reporting "1.2.1"
|
||||
apollo-server-caching "0.4.0"
|
||||
apollo-server-env "2.3.0"
|
||||
apollo-server-env "2.4.0"
|
||||
apollo-server-errors "2.3.0"
|
||||
apollo-server-plugin-base "0.4.1"
|
||||
apollo-tracing "0.6.1"
|
||||
apollo-server-plugin-base "0.5.1"
|
||||
apollo-tracing "0.7.1"
|
||||
fast-json-stable-stringify "^2.0.0"
|
||||
graphql-extensions "0.6.1"
|
||||
graphql-extensions "0.7.1"
|
||||
graphql-subscriptions "^1.0.0"
|
||||
graphql-tag "^2.9.2"
|
||||
graphql-tools "^4.0.0"
|
||||
@ -1491,10 +1475,10 @@ apollo-server-core@^1.3.6, apollo-server-core@^1.4.0:
|
||||
apollo-tracing "^0.1.0"
|
||||
graphql-extensions "^0.0.x"
|
||||
|
||||
apollo-server-env@2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.3.0.tgz#f0bf4484a6cc331a8c13763ded56e91beb16ba17"
|
||||
integrity sha512-WIwlkCM/gir0CkoYWPMTCH8uGCCKB/aM074U1bKayvkFOBVO2VgG5x2kgsfkyF05IMQq2/GOTsKhNY7RnUEhTA==
|
||||
apollo-server-env@2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.4.0.tgz#6611556c6b627a1636eed31317d4f7ea30705872"
|
||||
integrity sha512-7ispR68lv92viFeu5zsRUVGP+oxsVI3WeeBNniM22Cx619maBUwcYTIC3+Y3LpXILhLZCzA1FASZwusgSlyN9w==
|
||||
dependencies:
|
||||
node-fetch "^2.1.2"
|
||||
util.promisify "^1.0.0"
|
||||
@ -1504,10 +1488,10 @@ apollo-server-errors@2.3.0:
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061"
|
||||
integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw==
|
||||
|
||||
apollo-server-express@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.5.1.tgz#b112d9795f2fb39076d9cbc109f5eeb7835bed6b"
|
||||
integrity sha512-528wDQnOMIenDaICkYPFWQykdXQZwpygxd+Ar0PmZiaST042NSVExV4iRWI09p1THqfsuyHygqpkK+K94bUtBA==
|
||||
apollo-server-express@2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.1.tgz#1e2649d3fd38c0c0a2c830090fd41e086b259c9f"
|
||||
integrity sha512-TVu68LVp+COMGOXuxc0OFeCUQiPApxy7Isv2Vk85nikZV4t4FXlODB6PrRKf5rfvP31dvGsfE6GHPJTLLbKfyg==
|
||||
dependencies:
|
||||
"@apollographql/graphql-playground-html" "1.6.20"
|
||||
"@types/accepts" "^1.3.5"
|
||||
@ -1515,7 +1499,7 @@ apollo-server-express@2.5.1:
|
||||
"@types/cors" "^2.8.4"
|
||||
"@types/express" "4.16.1"
|
||||
accepts "^1.3.5"
|
||||
apollo-server-core "2.5.1"
|
||||
apollo-server-core "2.6.1"
|
||||
body-parser "^1.18.3"
|
||||
cors "^2.8.4"
|
||||
graphql-subscriptions "^1.0.0"
|
||||
@ -1543,36 +1527,36 @@ apollo-server-module-graphiql@^1.3.4, apollo-server-module-graphiql@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec"
|
||||
integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA==
|
||||
|
||||
apollo-server-plugin-base@0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.4.1.tgz#be380b28d71ad3b6b146d0d6a8f7ebf5675b07ff"
|
||||
integrity sha512-D2G6Ca/KBdQgEbmSfYqZqYbdVJnk/rrSv7Vj2NntwjfL7WJf0TjufxYJlrTH5jF6xCbsszDNGqfmt2Nm8x/o4g==
|
||||
apollo-server-plugin-base@0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.1.tgz#b81056666763879bdc98d8d58f3c4c43cbb30da6"
|
||||
integrity sha512-UejnBk6XDqYQ+Ydkbm+gvlOzP+doQA8glVUULs8rCi0/MshsFSsBVl6rtzruELDBVgZhJgGsd4pUexcvNc3aZA==
|
||||
|
||||
apollo-server-testing@~2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.5.1.tgz#9f210caa738a275a30269b6d8a3b29bce01b69a1"
|
||||
integrity sha512-npaj92Z33sNt4beBuoLJqlicqtawqI/41CA9/IrcswI9WwlWIxNKhfpcMYo9MpQWRT8aCIGOOAkBdG2jZlhH4Q==
|
||||
apollo-server-testing@~2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server-testing/-/apollo-server-testing-2.6.1.tgz#447f34980819fa52b120f26c632fab4efc55435b"
|
||||
integrity sha512-Qq0u79uKw3g14bq0nGxtUUiueFOv2ETkAax2mum+3f9Lm85VXELkY6c9bCWDVGjkUtt9Aog5qwSzWELb1KiUug==
|
||||
dependencies:
|
||||
apollo-server-core "2.5.1"
|
||||
apollo-server-core "2.6.1"
|
||||
|
||||
apollo-server@~2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.5.1.tgz#bfcfbebc123f692c0e6d85b0c56739646bd1bb7e"
|
||||
integrity sha512-eH3ubq300xhpFAxek28kb+5WZINXpWcwzyNqBQDbuasTlW8qSsqY7xrV6IIz6WUYKdX+ET0mx+Ta1DdaYQPrqw==
|
||||
apollo-server@~2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.1.tgz#1b1fc6020b75c0913550da5fa0f2005c62f1bc53"
|
||||
integrity sha512-Ed0zZjluRYPMC3Yr6oXQjcR11izu86nkjiS2MhjJA1mF8IXJfxbPp2hnX4Jf4vXPSkOP2e5ZHw0cdaIcu9GnRw==
|
||||
dependencies:
|
||||
apollo-server-core "2.5.1"
|
||||
apollo-server-express "2.5.1"
|
||||
apollo-server-core "2.6.1"
|
||||
apollo-server-express "2.6.1"
|
||||
express "^4.0.0"
|
||||
graphql-subscriptions "^1.0.0"
|
||||
graphql-tools "^4.0.0"
|
||||
|
||||
apollo-tracing@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.6.1.tgz#48a6d6040f9b2f2b4365a890c2e97cb763eb2392"
|
||||
integrity sha512-rrDBgTHa9GDA3wY8O7rDsFwC6ePIVzRGxpUsThgmLvIVkkCr0KS4wJJ4C01c+v4xsOXNuQwx0IyYhxZt4twwcA==
|
||||
apollo-tracing@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.1.tgz#6a7356b619f3aa0ca22c623b5d8bb1af5ca1c74c"
|
||||
integrity sha512-1BYQua+jCWFkZZJP0/rSpzY4XbLLbCrRHCYu8sJn0RCH/hs34BMdFXscS9uSglgIpXwUAIafgsU0hAVCrJjbTw==
|
||||
dependencies:
|
||||
apollo-server-env "2.3.0"
|
||||
graphql-extensions "0.6.1"
|
||||
apollo-server-env "2.4.0"
|
||||
graphql-extensions "0.7.1"
|
||||
|
||||
apollo-tracing@^0.1.0:
|
||||
version "0.1.4"
|
||||
@ -2441,11 +2425,6 @@ core-js-pure@3.1.2:
|
||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.2.tgz#62fc435f35b7374b9b782013cdcb2f97e9f6dffa"
|
||||
integrity sha512-5ckIdBF26B3ldK9PM177y2ZcATP2oweam9RskHSoqfZCrJ2As6wVg8zJ1zTriFsZf6clj/N1ThDFRGaomMsh9w==
|
||||
|
||||
core-js@3.0.0-beta.13:
|
||||
version "3.0.0-beta.13"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.13.tgz#7732c69be5e4758887917235fe7c0352c4cb42a1"
|
||||
integrity sha512-16Q43c/3LT9NyePUJKL8nRIQgYWjcBhjJSMWg96PVSxoS0PeE0NHitPI3opBrs9MGGHjte1KoEVr9W63YKlTXQ==
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.3, core-js@^2.5.7:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.2.tgz#267988d7268323b349e20b4588211655f0e83944"
|
||||
@ -3749,12 +3728,12 @@ graphql-deduplicator@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/graphql-deduplicator/-/graphql-deduplicator-2.0.2.tgz#d8608161cf6be97725e178df0c41f6a1f9f778f3"
|
||||
integrity sha512-0CGmTmQh4UvJfsaTPppJAcHwHln8Ayat7yXXxdnuWT+Mb1dBzkbErabCWzjXyKh/RefqlGTTA7EQOZHofMaKJA==
|
||||
|
||||
graphql-extensions@0.6.1:
|
||||
version "0.6.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.6.1.tgz#e61c4cb901e336dc5993a61093a8678a021dda59"
|
||||
integrity sha512-vB2WNQJn99pncHfvxgcdyVoazmG3cD8XzkgcaDrHTvV+xJGJEBP6056EWi0mNt1d6ukYyRS2zexdekmMCjcq0w==
|
||||
graphql-extensions@0.7.1:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.1.tgz#f55b01ac8ddf09a215e21f34caeee3ae66a88f21"
|
||||
integrity sha512-4NkAz/f0j5a1DSPl3V77OcesBmwhHz56Soj0yTImlcDdRv9knyO2e+ehi1TIeKBOyIKS7d3A7zqOW/4ieGxlVA==
|
||||
dependencies:
|
||||
"@apollographql/apollo-tools" "^0.3.6-alpha.1"
|
||||
"@apollographql/apollo-tools" "^0.3.6"
|
||||
|
||||
graphql-extensions@^0.0.x, graphql-extensions@~0.0.9:
|
||||
version "0.0.10"
|
||||
|
||||
@ -12,5 +12,6 @@
|
||||
# On Windows this resolves to C:\Users\dornhoeschen\AppData\Local\Temp\mongo-export (MinGW)
|
||||
EXPORT_PATH='/tmp/mongo-export/'
|
||||
EXPORT_MONGOEXPORT_BIN='mongoexport'
|
||||
MONGO_EXPORT_SPLIT_SIZE=100
|
||||
# On Windows use something like this
|
||||
# EXPORT_MONGOEXPORT_BIN='C:\Program Files\MongoDB\Server\3.6\bin\mongoexport.exe'
|
||||
# EXPORT_MONGOEXPORT_BIN='C:\Program Files\MongoDB\Server\3.6\bin\mongoexport.exe'
|
||||
|
||||
@ -10,7 +10,7 @@ set +o allexport
|
||||
function export_collection () {
|
||||
"${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --collection $1 --out "${EXPORT_PATH}$1.json"
|
||||
mkdir -p ${EXPORT_PATH}splits/$1/
|
||||
split -l 1000 -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/
|
||||
split -l ${MONGO_EXPORT_SPLIT_SIZE} -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/
|
||||
}
|
||||
|
||||
# Delete old export & ensure directory
|
||||
|
||||
@ -83,17 +83,17 @@ import_collection "contributions"
|
||||
import_collection "shouts"
|
||||
import_collection "comments"
|
||||
|
||||
import_collection "emotions"
|
||||
import_collection "invites"
|
||||
import_collection "notifications"
|
||||
import_collection "organizations"
|
||||
import_collection "pages"
|
||||
import_collection "projects"
|
||||
import_collection "settings"
|
||||
import_collection "status"
|
||||
import_collection "systemnotifications"
|
||||
import_collection "userscandos"
|
||||
import_collection "usersettings"
|
||||
# import_collection "emotions"
|
||||
# import_collection "invites"
|
||||
# import_collection "notifications"
|
||||
# import_collection "organizations"
|
||||
# import_collection "pages"
|
||||
# import_collection "projects"
|
||||
# import_collection "settings"
|
||||
# import_collection "status"
|
||||
# import_collection "systemnotifications"
|
||||
# import_collection "userscandos"
|
||||
# import_collection "usersettings"
|
||||
|
||||
echo "DONE"
|
||||
|
||||
|
||||
@ -30,6 +30,7 @@ services:
|
||||
- "MONGODB_AUTH_DB=${MONGODB_AUTH_DB}"
|
||||
- "MONGODB_DATABASE=${MONGODB_DATABASE}"
|
||||
- "UPLOADS_DIRECTORY=${UPLOADS_DIRECTORY}"
|
||||
- "MONGO_EXPORT_SPLIT_SIZE=${MONGO_EXPORT_SPLIT_SIZE}"
|
||||
ports:
|
||||
- 7687:7687
|
||||
- 7474:7474
|
||||
|
||||
@ -2,5 +2,7 @@
|
||||
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
|
||||
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-backend:latest $TRAVIS_BUILD_DIR/backend
|
||||
docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-web:latest $TRAVIS_BUILD_DIR/webapp
|
||||
docker build -t humanconnection/nitro-maintenance-worker:latest $TRAVIS_BUILD_DIR/deployment/legacy-migration/maintenance-worker
|
||||
docker push humanconnection/nitro-backend:latest
|
||||
docker push humanconnection/nitro-web:latest
|
||||
docker push humanconnection/nitro-maintenance-worker:latest
|
||||
|
||||
@ -10,7 +10,7 @@ module.exports = {
|
||||
},
|
||||
extends: [
|
||||
'standard',
|
||||
'plugin:vue/recommended',
|
||||
'plugin:vue/essential',
|
||||
'plugin:prettier/recommended'
|
||||
],
|
||||
// required to lint *.vue files
|
||||
@ -25,7 +25,9 @@ module.exports = {
|
||||
'no-console': ['error'],
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'vue/component-name-in-template-casing': ['error', 'kebab-case'],
|
||||
'prettier/prettier': ['error'],
|
||||
'prettier/prettier': ['error', {
|
||||
htmlWhitespaceSensitivity: 'ignore'
|
||||
}],
|
||||
// 'newline-per-chained-call': [2]
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM node:10-alpine as base
|
||||
FROM node:12.3.1-alpine as base
|
||||
LABEL Description="Web Frontend of the Social Network Human-Connection.org" Vendor="Human-Connection gGmbH" Version="0.0.1" Maintainer="Human-Connection gGmbH (developer@human-connection.org)"
|
||||
|
||||
EXPOSE 3000
|
||||
@ -18,7 +18,7 @@ COPY . .
|
||||
|
||||
FROM base as build-and-test
|
||||
RUN cp .env.template .env
|
||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||
RUN yarn install --ignore-engines --production=false --frozen-lockfile --non-interactive
|
||||
RUN yarn run build
|
||||
|
||||
FROM base as production
|
||||
|
||||
@ -1,10 +1,5 @@
|
||||
<template>
|
||||
<ds-avatar
|
||||
:image="avatarUrl"
|
||||
:name="userName"
|
||||
class="avatar"
|
||||
:size="size"
|
||||
/>
|
||||
<ds-avatar :image="avatarUrl" :name="userName" class="avatar" :size="size" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -1,20 +1,7 @@
|
||||
<template>
|
||||
<div
|
||||
:class="[
|
||||
(badges.length === 2) && 'hc-badges-dual'
|
||||
]"
|
||||
class="hc-badges"
|
||||
>
|
||||
<div
|
||||
v-for="badge in badges"
|
||||
:key="badge.key"
|
||||
class="hc-badge-container"
|
||||
>
|
||||
<hc-image
|
||||
:title="badge.key"
|
||||
:image-props="{ src: badge.icon}"
|
||||
class="hc-badge"
|
||||
/>
|
||||
<div :class="[badges.length === 2 && 'hc-badges-dual']" class="hc-badges">
|
||||
<div v-for="badge in badges" :key="badge.key" class="hc-badge-container">
|
||||
<hc-image :title="badge.key" :image-props="{ src: badge.icon }" class="hc-badge" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-tag>
|
||||
<ds-icon
|
||||
size="large"
|
||||
:name="icon"
|
||||
/>
|
||||
<ds-icon size="large" :name="icon" />
|
||||
{{ name }}
|
||||
</ds-tag>
|
||||
</template>
|
||||
|
||||
@ -14,11 +14,20 @@ describe('Comment.vue', () => {
|
||||
let propsData
|
||||
let mocks
|
||||
let getters
|
||||
let wrapper
|
||||
let Wrapper
|
||||
|
||||
beforeEach(() => {
|
||||
propsData = {}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}
|
||||
getters = {
|
||||
'auth/user': () => {
|
||||
@ -29,11 +38,16 @@ describe('Comment.vue', () => {
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
const Wrapper = () => {
|
||||
Wrapper = () => {
|
||||
const store = new Vuex.Store({
|
||||
getters,
|
||||
})
|
||||
return shallowMount(Comment, { store, propsData, mocks, localVue })
|
||||
return shallowMount(Comment, {
|
||||
store,
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('given a comment', () => {
|
||||
@ -45,7 +59,7 @@ describe('Comment.vue', () => {
|
||||
})
|
||||
|
||||
it('renders content', () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.text()).toMatch('Hello I am a comment content')
|
||||
})
|
||||
|
||||
@ -55,17 +69,17 @@ describe('Comment.vue', () => {
|
||||
})
|
||||
|
||||
it('renders no comment data', () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.text()).not.toMatch('comment content')
|
||||
})
|
||||
|
||||
it('has no "disabled-content" css class', () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.classes()).not.toContain('disabled-content')
|
||||
})
|
||||
|
||||
it('translates a placeholder', () => {
|
||||
/* const wrapper = */ Wrapper()
|
||||
wrapper = Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['comment.content.unavailable-placeholder']]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
@ -77,16 +91,46 @@ describe('Comment.vue', () => {
|
||||
})
|
||||
|
||||
it('renders comment data', () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.text()).toMatch('comment content')
|
||||
})
|
||||
|
||||
it('has a "disabled-content" css class', () => {
|
||||
const wrapper = Wrapper()
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.classes()).toContain('disabled-content')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test callbacks', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('deletion of Comment from List by invoking "deleteCommentCallback()"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deleteCommentCallback()
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('emits "deleteComment"', () => {
|
||||
expect(wrapper.emitted().deleteComment.length).toBe(1)
|
||||
})
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,17 +1,11 @@
|
||||
<template>
|
||||
<div v-if="(comment.deleted || comment.disabled) && !isModerator">
|
||||
<ds-text
|
||||
style="padding-left: 40px; font-weight: bold;"
|
||||
color="soft"
|
||||
>
|
||||
<ds-text style="padding-left: 40px; font-weight: bold;" color="soft">
|
||||
<ds-icon name="ban" />
|
||||
{{ this.$t('comment.content.unavailable-placeholder') }}
|
||||
</ds-text>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
:class="{'comment': true, 'disabled-content': (comment.deleted || comment.disabled)}"
|
||||
>
|
||||
<div v-else :class="{ comment: true, 'disabled-content': comment.deleted || comment.disabled }">
|
||||
<ds-space margin-bottom="x-small">
|
||||
<hc-user :user="author" />
|
||||
</ds-space>
|
||||
@ -20,6 +14,7 @@
|
||||
placement="bottom-end"
|
||||
resource-type="comment"
|
||||
:resource="comment"
|
||||
:callbacks="{ confirm: deleteCommentCallback, cancel: null }"
|
||||
style="float-right"
|
||||
:is-owner="isAuthor(author.id)"
|
||||
/>
|
||||
@ -27,15 +22,13 @@
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<ds-space margin-bottom="small" />
|
||||
<div
|
||||
style="padding-left: 40px;"
|
||||
v-html="comment.contentExcerpt"
|
||||
/>
|
||||
<div style="padding-left: 40px;" v-html="comment.contentExcerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { mapGetters } from 'vuex'
|
||||
import HcUser from '~/components/User'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
@ -70,6 +63,25 @@ export default {
|
||||
isAuthor(id) {
|
||||
return this.user.id === id
|
||||
},
|
||||
async deleteCommentCallback() {
|
||||
try {
|
||||
var gqlMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeleteComment(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
await this.$apollo.mutate({
|
||||
mutation: gqlMutation,
|
||||
variables: { id: this.comment.id },
|
||||
})
|
||||
this.$toast.success(this.$t(`delete.comment.success`))
|
||||
this.$emit('deleteComment')
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -1,32 +1,13 @@
|
||||
<template>
|
||||
<dropdown
|
||||
class="content-menu"
|
||||
:placement="placement"
|
||||
offset="5"
|
||||
>
|
||||
<template
|
||||
slot="default"
|
||||
slot-scope="{toggleMenu}"
|
||||
>
|
||||
<slot
|
||||
name="button"
|
||||
:toggleMenu="toggleMenu"
|
||||
>
|
||||
<ds-button
|
||||
class="content-menu-trigger"
|
||||
size="small"
|
||||
ghost
|
||||
@click.prevent="toggleMenu"
|
||||
>
|
||||
<dropdown class="content-menu" :placement="placement" offset="5">
|
||||
<template slot="default" slot-scope="{ toggleMenu }">
|
||||
<slot name="button" :toggleMenu="toggleMenu">
|
||||
<ds-button class="content-menu-trigger" size="small" ghost @click.prevent="toggleMenu">
|
||||
<ds-icon name="ellipsis-v" />
|
||||
</ds-button>
|
||||
</slot>
|
||||
</template>
|
||||
<div
|
||||
slot="popover"
|
||||
slot-scope="{toggleMenu}"
|
||||
class="content-menu-popover"
|
||||
>
|
||||
<div slot="popover" slot-scope="{ toggleMenu }" class="content-menu-popover">
|
||||
<ds-menu :routes="routes">
|
||||
<ds-menu-item
|
||||
slot="menuitem"
|
||||
@ -47,6 +28,7 @@
|
||||
import Dropdown from '~/components/Dropdown'
|
||||
|
||||
export default {
|
||||
name: 'ContentMenu',
|
||||
components: {
|
||||
Dropdown,
|
||||
},
|
||||
@ -61,6 +43,7 @@ export default {
|
||||
return value.match(/(contribution|comment|organization|user)/)
|
||||
},
|
||||
},
|
||||
callbacks: { type: Object, required: true },
|
||||
},
|
||||
computed: {
|
||||
routes() {
|
||||
@ -68,7 +51,7 @@ export default {
|
||||
|
||||
if (this.isOwner && this.resourceType === 'contribution') {
|
||||
routes.push({
|
||||
name: this.$t(`contribution.edit`),
|
||||
name: this.$t(`post.menu.edit`),
|
||||
path: this.$router.resolve({
|
||||
name: 'post-edit-id',
|
||||
params: {
|
||||
@ -78,21 +61,29 @@ export default {
|
||||
icon: 'edit',
|
||||
})
|
||||
routes.push({
|
||||
name: this.$t(`post.delete.title`),
|
||||
name: this.$t(`post.menu.delete`),
|
||||
callback: () => {
|
||||
this.openModal('delete')
|
||||
},
|
||||
icon: 'trash',
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isOwner && this.resourceType === 'comment') {
|
||||
// routes.push({
|
||||
// name: this.$t(`comment.menu.edit`),
|
||||
// callback: () => {
|
||||
// /* eslint-disable-next-line no-console */
|
||||
// console.log('EDIT COMMENT')
|
||||
// },
|
||||
// icon: 'edit'
|
||||
// })
|
||||
routes.push({
|
||||
name: this.$t(`comment.edit`),
|
||||
name: this.$t(`comment.menu.delete`),
|
||||
callback: () => {
|
||||
/* eslint-disable-next-line no-console */
|
||||
console.log('EDIT COMMENT')
|
||||
this.openModal('delete')
|
||||
},
|
||||
icon: 'edit',
|
||||
icon: 'trash',
|
||||
})
|
||||
}
|
||||
|
||||
@ -144,6 +135,7 @@ export default {
|
||||
data: {
|
||||
type: this.resourceType,
|
||||
resource: this.resource,
|
||||
callbacks: this.callbacks,
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@ -1,35 +1,13 @@
|
||||
<template>
|
||||
<ds-form
|
||||
ref="contributionForm"
|
||||
v-model="form"
|
||||
:schema="formSchema"
|
||||
@submit="submit"
|
||||
>
|
||||
<ds-form ref="contributionForm" v-model="form" :schema="formSchema" @submit="submit">
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-card>
|
||||
<ds-input
|
||||
model="title"
|
||||
class="post-title"
|
||||
placeholder="Title"
|
||||
name="title"
|
||||
autofocus
|
||||
/>
|
||||
<ds-input model="title" class="post-title" placeholder="Title" name="title" autofocus />
|
||||
<no-ssr>
|
||||
<hc-editor
|
||||
:users="users"
|
||||
:value="form.content"
|
||||
@input="updateEditorContent"
|
||||
/>
|
||||
<hc-editor :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
</no-ssr>
|
||||
<div
|
||||
slot="footer"
|
||||
style="text-align: right"
|
||||
>
|
||||
<ds-button
|
||||
:disabled="loading || disabled"
|
||||
ghost
|
||||
@click.prevent="$router.back()"
|
||||
>
|
||||
<div slot="footer" style="text-align: right">
|
||||
<ds-button :disabled="loading || disabled" ghost @click.prevent="$router.back()">
|
||||
{{ $t('actions.cancel') }}
|
||||
</ds-button>
|
||||
<ds-button
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<span>
|
||||
<no-ssr
|
||||
placeholder="0"
|
||||
tag="span"
|
||||
>
|
||||
<no-ssr placeholder="0" tag="span">
|
||||
<count-to
|
||||
:start-val="lastEndVal || startVal"
|
||||
:end-val="endVal"
|
||||
|
||||
@ -7,17 +7,8 @@
|
||||
trigger="manual"
|
||||
:offset="offset"
|
||||
>
|
||||
<slot
|
||||
:toggleMenu="toggleMenu"
|
||||
:openMenu="openMenu"
|
||||
:closeMenu="closeMenu"
|
||||
:isOpen="isOpen"
|
||||
/>
|
||||
<div
|
||||
slot="popover"
|
||||
@mouseover="popoverMouseEnter"
|
||||
@mouseleave="popoveMouseLeave"
|
||||
>
|
||||
<slot :toggleMenu="toggleMenu" :openMenu="openMenu" :closeMenu="closeMenu" :isOpen="isOpen" />
|
||||
<div slot="popover" @mouseover="popoverMouseEnter" @mouseleave="popoveMouseLeave">
|
||||
<slot
|
||||
name="popover"
|
||||
:toggleMenu="toggleMenu"
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
<template>
|
||||
<div class="editor">
|
||||
<div
|
||||
v-show="showSuggestions"
|
||||
ref="suggestions"
|
||||
class="suggestion-list"
|
||||
>
|
||||
<div v-show="showSuggestions" ref="suggestions" class="suggestion-list">
|
||||
<template v-if="hasResults">
|
||||
<div
|
||||
v-for="(user, index) in filteredUsers"
|
||||
@ -16,10 +12,7 @@
|
||||
@{{ user.slug }}
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
v-else
|
||||
class="suggestion-list__item is-empty"
|
||||
>
|
||||
<div v-else class="suggestion-list__item is-empty">
|
||||
No users found
|
||||
</div>
|
||||
</div>
|
||||
@ -154,10 +147,7 @@
|
||||
</ds-button>
|
||||
</div>
|
||||
</editor-floating-menu>
|
||||
<editor-content
|
||||
ref="editor"
|
||||
:editor="editor"
|
||||
/>
|
||||
<editor-content ref="editor" :editor="editor" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
<template>
|
||||
<ds-space
|
||||
class="hc-empty"
|
||||
centered
|
||||
:margin="margin"
|
||||
>
|
||||
<ds-space class="hc-empty" centered :margin="margin">
|
||||
<ds-text>
|
||||
<img
|
||||
:src="iconPath"
|
||||
@ -11,12 +7,9 @@
|
||||
class="hc-empty-icon"
|
||||
style="margin-bottom: 5px"
|
||||
alt="Empty"
|
||||
><br>
|
||||
<ds-text
|
||||
v-show="message"
|
||||
class="hc-empty-message"
|
||||
color="softer"
|
||||
>
|
||||
/>
|
||||
<br />
|
||||
<ds-text v-show="message" class="hc-empty-message" color="softer">
|
||||
{{ message }}
|
||||
</ds-text>
|
||||
</ds-text>
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<img
|
||||
v-bind="imageProps"
|
||||
:src="imageSrc"
|
||||
>
|
||||
<img v-bind="imageProps" :src="imageSrc" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@ -1,14 +1,6 @@
|
||||
<template>
|
||||
<ds-space
|
||||
margin-top="large"
|
||||
style="text-align: center"
|
||||
>
|
||||
<ds-button
|
||||
:loading="loading"
|
||||
icon="arrow-down"
|
||||
ghost
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<ds-space margin-top="large" style="text-align: center">
|
||||
<ds-button :loading="loading" icon="arrow-down" ghost @click="$emit('click')">
|
||||
{{ $t('actions.loadMore') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
|
||||
@ -1,30 +1,19 @@
|
||||
<template>
|
||||
<dropdown
|
||||
ref="menu"
|
||||
:placement="placement"
|
||||
:offset="offset"
|
||||
>
|
||||
<dropdown ref="menu" :placement="placement" :offset="offset">
|
||||
<a
|
||||
slot="default"
|
||||
slot-scope="{toggleMenu}"
|
||||
slot-scope="{ toggleMenu }"
|
||||
class="locale-menu"
|
||||
href="#"
|
||||
@click.prevent="toggleMenu()"
|
||||
>
|
||||
<ds-icon
|
||||
style="margin-right: 2px;"
|
||||
name="globe"
|
||||
/>
|
||||
<ds-icon style="margin-right: 2px;" name="globe" />
|
||||
{{ current.code.toUpperCase() }}
|
||||
<ds-icon
|
||||
style="margin-left: 2px"
|
||||
size="xx-small"
|
||||
name="angle-down"
|
||||
/>
|
||||
<ds-icon style="margin-left: 2px" size="xx-small" name="angle-down" />
|
||||
</a>
|
||||
<ds-menu
|
||||
slot="popover"
|
||||
slot-scope="{toggleMenu}"
|
||||
slot-scope="{ toggleMenu }"
|
||||
class="locale-menu-popover"
|
||||
:matcher="matcher"
|
||||
:routes="routes"
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import Modal from './Modal.vue'
|
||||
import DeleteModal from './Modal/DeleteModal.vue'
|
||||
import DisableModal from './Modal/DisableModal.vue'
|
||||
import ReportModal from './Modal/ReportModal.vue'
|
||||
import Vuex from 'vuex'
|
||||
@ -29,7 +30,11 @@ describe('Modal.vue', () => {
|
||||
'modal/SET_OPEN': mutations.SET_OPEN,
|
||||
},
|
||||
})
|
||||
return mountMethod(Modal, { store, mocks, localVue })
|
||||
return mountMethod(Modal, {
|
||||
store,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,6 +60,7 @@ describe('Modal.vue', () => {
|
||||
|
||||
it('initially empty', () => {
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.contains(DeleteModal)).toBe(false)
|
||||
expect(wrapper.contains(DisableModal)).toBe(false)
|
||||
expect(wrapper.contains(ReportModal)).toBe(false)
|
||||
})
|
||||
@ -69,6 +75,10 @@ describe('Modal.vue', () => {
|
||||
id: 'c456',
|
||||
title: 'some title',
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
@ -83,6 +93,10 @@ describe('Modal.vue', () => {
|
||||
type: 'contribution',
|
||||
name: 'some title',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@ -97,23 +111,49 @@ describe('Modal.vue', () => {
|
||||
it('passes author name to disable modal', () => {
|
||||
state.data = {
|
||||
type: 'comment',
|
||||
resource: { id: 'c456', author: { name: 'Author name' } },
|
||||
resource: {
|
||||
id: 'c456',
|
||||
author: {
|
||||
name: 'Author name',
|
||||
},
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find(DisableModal).props()).toEqual({
|
||||
type: 'comment',
|
||||
name: 'Author name',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('does not crash if author is undefined', () => {
|
||||
state.data = { type: 'comment', resource: { id: 'c456' } }
|
||||
state.data = {
|
||||
type: 'comment',
|
||||
resource: {
|
||||
id: 'c456',
|
||||
},
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
expect(wrapper.find(DisableModal).props()).toEqual({
|
||||
type: 'comment',
|
||||
name: '',
|
||||
id: 'c456',
|
||||
callbacks: {
|
||||
confirm: null,
|
||||
cancel: null,
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<div class="modal-wrapper">
|
||||
<!-- Todo: Put all modals with 2 buttons and equal properties in one customiced 'danger-action-modal' -->
|
||||
<disable-modal
|
||||
v-if="open === 'disable'"
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
@close="close"
|
||||
/>
|
||||
<report-modal
|
||||
@ -12,6 +14,7 @@
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
@close="close"
|
||||
/>
|
||||
<delete-modal
|
||||
@ -19,15 +22,16 @@
|
||||
:id="data.resource.id"
|
||||
:type="data.type"
|
||||
:name="name"
|
||||
:callbacks="data.callbacks"
|
||||
@close="close"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DeleteModal from '~/components/Modal/DeleteModal'
|
||||
import DisableModal from '~/components/Modal/DisableModal'
|
||||
import ReportModal from '~/components/Modal/ReportModal'
|
||||
import DeleteModal from '~/components/Modal/DeleteModal'
|
||||
import { mapGetters } from 'vuex'
|
||||
|
||||
export default {
|
||||
|
||||
@ -2,17 +2,14 @@ import { shallowMount, mount, createLocalVue } from '@vue/test-utils'
|
||||
import DeleteModal from './DeleteModal.vue'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
import VueRouter from 'vue-router'
|
||||
|
||||
const routes = [{ path: '/' }]
|
||||
const router = new VueRouter({ routes })
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
localVue.use(VueRouter)
|
||||
|
||||
describe('DeleteModal.vue', () => {
|
||||
let Wrapper
|
||||
let wrapper
|
||||
let propsData
|
||||
let mocks
|
||||
@ -20,79 +17,113 @@ describe('DeleteModal.vue', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
type: 'contribution',
|
||||
id: 'c300',
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
$toast: {
|
||||
success: () => {},
|
||||
error: () => {},
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
const Wrapper = () => {
|
||||
return shallowMount(DeleteModal, { propsData, mocks, localVue, router })
|
||||
Wrapper = () => {
|
||||
return shallowMount(DeleteModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('defaults', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('success false', () => {
|
||||
expect(Wrapper().vm.success).toBe(false)
|
||||
expect(wrapper.vm.success).toBe(false)
|
||||
})
|
||||
|
||||
it('loading false', () => {
|
||||
expect(Wrapper().vm.loading).toBe(false)
|
||||
expect(wrapper.vm.loading).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a post', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'contribution',
|
||||
id: 'p23',
|
||||
type: 'post',
|
||||
name: 'It is a post',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('mentions post title', () => {
|
||||
Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['post.delete.message', { name: 'It is a post' }]]
|
||||
const expected = [
|
||||
[
|
||||
'delete.contribution.message',
|
||||
{
|
||||
name: 'It is a post',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a comment', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'comment',
|
||||
id: 'c4',
|
||||
name: 'It is the user of the comment',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
it('mentions comments user name', () => {
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [
|
||||
[
|
||||
'delete.comment.message',
|
||||
{
|
||||
name: 'It is the user of the comment',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return mount(DeleteModal, { propsData, mocks, localVue, router })
|
||||
Wrapper = () => {
|
||||
return mount(DeleteModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
it('renders', () => {
|
||||
expect(Wrapper().is('div')).toBe(true)
|
||||
})
|
||||
|
||||
describe('given id', () => {
|
||||
describe('given post id', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
type: 'user',
|
||||
id: 'u3',
|
||||
}
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('click cancel button', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.find('button.cancel').trigger('click')
|
||||
})
|
||||
|
||||
@ -103,12 +134,12 @@ describe('DeleteModal.vue', () => {
|
||||
expect(wrapper.vm.isOpen).toBe(false)
|
||||
})
|
||||
|
||||
it('emits "close"', () => {
|
||||
expect(wrapper.emitted().close).toBeTruthy()
|
||||
it('does call the cancel callback', () => {
|
||||
expect(propsData.callbacks.cancel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('does not call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).not.toHaveBeenCalled()
|
||||
it('emits "close"', () => {
|
||||
expect(wrapper.emitted().close).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -118,20 +149,10 @@ describe('DeleteModal.vue', () => {
|
||||
wrapper.find('button.confirm').trigger('click')
|
||||
})
|
||||
|
||||
it('calls delete mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('sets success', () => {
|
||||
expect(wrapper.vm.success).toBe(true)
|
||||
})
|
||||
|
||||
it('displays a success message', () => {
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['post.delete.success']]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
@ -139,6 +160,9 @@ describe('DeleteModal.vue', () => {
|
||||
expect(wrapper.vm.isOpen).toBe(false)
|
||||
})
|
||||
|
||||
it('does call the confirm callback', () => {
|
||||
expect(propsData.callbacks.confirm).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
it('emits close', () => {
|
||||
expect(wrapper.emitted().close).toBeTruthy()
|
||||
})
|
||||
|
||||
@ -1,15 +1,7 @@
|
||||
<template>
|
||||
<ds-modal
|
||||
:title="title"
|
||||
:is-open="isOpen"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
|
||||
<transition name="ds-transition-fade">
|
||||
<ds-flex
|
||||
v-if="success"
|
||||
class="hc-modal-success"
|
||||
centered
|
||||
>
|
||||
<ds-flex v-if="success" class="hc-modal-success" centered>
|
||||
<sweetalert-icon icon="success" />
|
||||
</ds-flex>
|
||||
</transition>
|
||||
@ -17,32 +9,17 @@
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-html="message" />
|
||||
|
||||
<template
|
||||
slot="footer"
|
||||
>
|
||||
<ds-button
|
||||
class="cancel"
|
||||
icon="close"
|
||||
@click="cancel"
|
||||
>
|
||||
{{ $t('post.delete.cancel') }}
|
||||
</ds-button>
|
||||
<template slot="footer">
|
||||
<ds-button class="cancel" icon="close" @click="cancel">{{ $t('delete.cancel') }}</ds-button>
|
||||
|
||||
<ds-button
|
||||
danger
|
||||
class="confirm"
|
||||
icon="trash"
|
||||
:loading="loading"
|
||||
@click="confirm"
|
||||
>
|
||||
{{ $t('post.delete.submit') }}
|
||||
<ds-button danger class="confirm" icon="trash" :loading="loading" @click="confirm">
|
||||
{{ $t('delete.submit') }}
|
||||
</ds-button>
|
||||
</template>
|
||||
</ds-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import { SweetalertIcon } from 'vue-sweetalert-icons'
|
||||
|
||||
export default {
|
||||
@ -53,6 +30,7 @@ export default {
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -64,15 +42,18 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
return this.$t(`post.delete.title`)
|
||||
return this.$t(`delete.${this.type}.title`)
|
||||
},
|
||||
message() {
|
||||
const name = this.$filters.truncate(this.name, 30)
|
||||
return this.$t(`post.delete.message`, { name })
|
||||
return this.$t(`delete.${this.type}.message`, { name })
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -81,35 +62,19 @@ export default {
|
||||
async confirm() {
|
||||
this.loading = true
|
||||
try {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
DeletePost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { id: this.id },
|
||||
})
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
this.success = true
|
||||
this.$toast.success(this.$t('post.delete.success'))
|
||||
setTimeout(() => {
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.success = false
|
||||
this.$emit('close')
|
||||
if (this.$router.history.current.name === 'post-id-slug') {
|
||||
// redirect to index
|
||||
this.$router.history.push('/')
|
||||
} else {
|
||||
// reload the page (when deleting from profile or index)
|
||||
window.location.assign(window.location.href)
|
||||
}
|
||||
}, 500)
|
||||
}, 1500)
|
||||
} catch (err) {
|
||||
this.success = false
|
||||
this.$toast.error(err.message)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
|
||||
@ -14,8 +14,12 @@ describe('DisableModal.vue', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
type: 'contribution',
|
||||
name: 'blah',
|
||||
id: 'c42',
|
||||
name: 'blah',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
}
|
||||
mocks = {
|
||||
$filters: {
|
||||
@ -34,22 +38,34 @@ describe('DisableModal.vue', () => {
|
||||
|
||||
describe('shallowMount', () => {
|
||||
const Wrapper = () => {
|
||||
return shallowMount(DisableModal, { propsData, mocks, localVue })
|
||||
return shallowMount(DisableModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('given a user', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'user',
|
||||
id: 'u2',
|
||||
name: 'Bob Ross',
|
||||
id: 'u2',
|
||||
}
|
||||
})
|
||||
|
||||
it('mentions user name', () => {
|
||||
Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['disable.user.message', { name: 'Bob Ross' }]]
|
||||
const expected = [
|
||||
[
|
||||
'disable.user.message',
|
||||
{
|
||||
name: 'Bob Ross',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
@ -57,16 +73,24 @@ describe('DisableModal.vue', () => {
|
||||
describe('given a contribution', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'contribution',
|
||||
id: 'c3',
|
||||
name: 'This is some post title.',
|
||||
id: 'c3',
|
||||
}
|
||||
})
|
||||
|
||||
it('mentions contribution title', () => {
|
||||
Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['disable.contribution.message', { name: 'This is some post title.' }]]
|
||||
const expected = [
|
||||
[
|
||||
'disable.contribution.message',
|
||||
{
|
||||
name: 'This is some post title.',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
@ -74,13 +98,18 @@ describe('DisableModal.vue', () => {
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return mount(DisableModal, { propsData, mocks, localVue })
|
||||
return mount(DisableModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('given id', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'user',
|
||||
id: 'u4711',
|
||||
}
|
||||
@ -126,7 +155,9 @@ describe('DisableModal.vue', () => {
|
||||
it('passes id to mutation', () => {
|
||||
const calls = mocks.$apollo.mutate.mock.calls
|
||||
const [[{ variables }]] = calls
|
||||
expect(variables).toEqual({ id: 'u4711' })
|
||||
expect(variables).toEqual({
|
||||
id: 'u4711',
|
||||
})
|
||||
})
|
||||
|
||||
it('fades away', () => {
|
||||
|
||||
@ -1,26 +1,14 @@
|
||||
<template>
|
||||
<ds-modal
|
||||
:title="title"
|
||||
:is-open="isOpen"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-html="message" />
|
||||
|
||||
<template slot="footer">
|
||||
<ds-button
|
||||
class="cancel"
|
||||
@click="cancel"
|
||||
>
|
||||
<ds-button class="cancel" @click="cancel">
|
||||
{{ $t('disable.cancel') }}
|
||||
</ds-button>
|
||||
|
||||
<ds-button
|
||||
danger
|
||||
class="confirm"
|
||||
icon="exclamation-circle"
|
||||
@click="confirm"
|
||||
>
|
||||
<ds-button danger class="confirm" icon="exclamation-circle" @click="confirm">
|
||||
{{ $t('disable.submit') }}
|
||||
</ds-button>
|
||||
</template>
|
||||
@ -31,9 +19,11 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
name: 'DisableModal',
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -53,7 +43,10 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
cancel() {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -61,6 +54,9 @@ export default {
|
||||
},
|
||||
async confirm() {
|
||||
try {
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
|
||||
@ -17,6 +17,10 @@ describe('ReportModal.vue', () => {
|
||||
propsData = {
|
||||
type: 'contribution',
|
||||
id: 'c43',
|
||||
callbacks: {
|
||||
confirm: jest.fn(),
|
||||
cancel: jest.fn(),
|
||||
},
|
||||
}
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
@ -35,7 +39,11 @@ describe('ReportModal.vue', () => {
|
||||
|
||||
describe('shallowMount', () => {
|
||||
const Wrapper = () => {
|
||||
return shallowMount(ReportModal, { propsData, mocks, localVue })
|
||||
return shallowMount(ReportModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
describe('defaults', () => {
|
||||
@ -51,6 +59,7 @@ describe('ReportModal.vue', () => {
|
||||
describe('given a user', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'user',
|
||||
id: 'u4',
|
||||
name: 'Bob Ross',
|
||||
@ -60,7 +69,14 @@ describe('ReportModal.vue', () => {
|
||||
it('mentions user name', () => {
|
||||
Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['report.user.message', { name: 'Bob Ross' }]]
|
||||
const expected = [
|
||||
[
|
||||
'report.user.message',
|
||||
{
|
||||
name: 'Bob Ross',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
@ -68,8 +84,9 @@ describe('ReportModal.vue', () => {
|
||||
describe('given a post', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
id: 'p23',
|
||||
...propsData,
|
||||
type: 'post',
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
}
|
||||
})
|
||||
@ -77,7 +94,14 @@ describe('ReportModal.vue', () => {
|
||||
it('mentions post title', () => {
|
||||
Wrapper()
|
||||
const calls = mocks.$t.mock.calls
|
||||
const expected = [['report.post.message', { name: 'It is a post' }]]
|
||||
const expected = [
|
||||
[
|
||||
'report.post.message',
|
||||
{
|
||||
name: 'It is a post',
|
||||
},
|
||||
],
|
||||
]
|
||||
expect(calls).toEqual(expect.arrayContaining(expected))
|
||||
})
|
||||
})
|
||||
@ -85,7 +109,11 @@ describe('ReportModal.vue', () => {
|
||||
|
||||
describe('mount', () => {
|
||||
const Wrapper = () => {
|
||||
return mount(ReportModal, { propsData, mocks, localVue })
|
||||
return mount(ReportModal, {
|
||||
propsData,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
@ -97,6 +125,7 @@ describe('ReportModal.vue', () => {
|
||||
describe('given id', () => {
|
||||
beforeEach(() => {
|
||||
propsData = {
|
||||
...propsData,
|
||||
type: 'user',
|
||||
id: 'u4711',
|
||||
}
|
||||
|
||||
@ -1,15 +1,7 @@
|
||||
<template>
|
||||
<ds-modal
|
||||
:title="title"
|
||||
:is-open="isOpen"
|
||||
@cancel="cancel"
|
||||
>
|
||||
<ds-modal :title="title" :is-open="isOpen" @cancel="cancel">
|
||||
<transition name="ds-transition-fade">
|
||||
<ds-flex
|
||||
v-if="success"
|
||||
class="hc-modal-success"
|
||||
centered
|
||||
>
|
||||
<ds-flex v-if="success" class="hc-modal-success" centered>
|
||||
<sweetalert-icon icon="success" />
|
||||
</ds-flex>
|
||||
</transition>
|
||||
@ -17,14 +9,8 @@
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<p v-html="message" />
|
||||
|
||||
<template
|
||||
slot="footer"
|
||||
>
|
||||
<ds-button
|
||||
class="cancel"
|
||||
icon="close"
|
||||
@click="cancel"
|
||||
>
|
||||
<template slot="footer">
|
||||
<ds-button class="cancel" icon="close" @click="cancel">
|
||||
{{ $t('report.cancel') }}
|
||||
</ds-button>
|
||||
|
||||
@ -53,6 +39,7 @@ export default {
|
||||
props: {
|
||||
name: { type: String, default: '' },
|
||||
type: { type: String, required: true },
|
||||
callbacks: { type: Object, required: true },
|
||||
id: { type: String, required: true },
|
||||
},
|
||||
data() {
|
||||
@ -73,6 +60,9 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async cancel() {
|
||||
if (this.callbacks.cancel) {
|
||||
await this.callbacks.cancel()
|
||||
}
|
||||
this.isOpen = false
|
||||
setTimeout(() => {
|
||||
this.$emit('close')
|
||||
@ -81,6 +71,9 @@ export default {
|
||||
async confirm() {
|
||||
this.loading = true
|
||||
try {
|
||||
if (this.callbacks.confirm) {
|
||||
await this.callbacks.confirm()
|
||||
}
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation($id: ID!) {
|
||||
|
||||
@ -27,11 +27,7 @@
|
||||
/>
|
||||
<password-strength :password="formData.newPassword" />
|
||||
<ds-space margin-top="base">
|
||||
<ds-button
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
primary
|
||||
>
|
||||
<ds-button :loading="loading" :disabled="disabled" primary>
|
||||
{{ $t('settings.security.change-password.button') }}
|
||||
</ds-button>
|
||||
</ds-space>
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
<template>
|
||||
<div class="field">
|
||||
<div class="password-strength-meter">
|
||||
<div
|
||||
class="password-strength-meter-inner"
|
||||
:class="'strength-' + strength"
|
||||
/>
|
||||
<div class="password-strength-meter-inner" :class="'strength-' + strength" />
|
||||
</div>
|
||||
<p class="help">
|
||||
<span
|
||||
v-if="pass"
|
||||
:class="{ insecure: !isSecure }"
|
||||
>
|
||||
<span v-if="pass" :class="{ insecure: !isSecure }">
|
||||
{{ $t('settings.security.change-password.passwordSecurity') }}:
|
||||
<strong>{{ $t(`settings.security.change-password.passwordStrength${strength}`) }}</strong>
|
||||
</span>
|
||||
|
||||
@ -1,78 +1,68 @@
|
||||
<template>
|
||||
<ds-card
|
||||
:image="post.image"
|
||||
:class="{'post-card': true, 'disabled-content': post.disabled}"
|
||||
>
|
||||
<!-- Post Link Target -->
|
||||
<nuxt-link
|
||||
class="post-link"
|
||||
:to="{ name: 'post-id-slug', params: { id: post.id, slug: post.slug } }"
|
||||
>
|
||||
{{ post.title }}
|
||||
</nuxt-link>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Username, Image & Date of Post -->
|
||||
<div>
|
||||
<no-ssr>
|
||||
<hc-user
|
||||
:user="post.author"
|
||||
:trunc="35"
|
||||
:date-time="post.createdAt"
|
||||
/>
|
||||
</no-ssr>
|
||||
<hc-ribbon :text="$t('post.name')" />
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Title -->
|
||||
<ds-heading
|
||||
tag="h3"
|
||||
no-margin
|
||||
>
|
||||
{{ post.title }}
|
||||
</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Content Excerpt -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<div
|
||||
class="hc-editor-content"
|
||||
v-html="excerpt"
|
||||
/>
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<!-- Footer o the Post -->
|
||||
<template slot="footer">
|
||||
<div style="display: inline-block; opacity: .5;">
|
||||
<!-- Categories -->
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{content: category.name, placement: 'bottom-start', delay: { show: 500 }}"
|
||||
:icon="category.icon"
|
||||
/>
|
||||
<ds-flex-item :width="width">
|
||||
<ds-card :image="post.image" :class="{ 'post-card': true, 'disabled-content': post.disabled }">
|
||||
<!-- Post Link Target -->
|
||||
<nuxt-link
|
||||
class="post-link"
|
||||
:to="{ name: 'post-id-slug', params: { id: post.id, slug: post.slug } }"
|
||||
>
|
||||
{{ post.title }}
|
||||
</nuxt-link>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Username, Image & Date of Post -->
|
||||
<div>
|
||||
<no-ssr>
|
||||
<hc-user :user="post.author" :trunc="35" :date-time="post.createdAt" />
|
||||
</no-ssr>
|
||||
<hc-ribbon :text="$t('post.name')" />
|
||||
</div>
|
||||
<no-ssr>
|
||||
<div style="display: inline-block; float: right">
|
||||
<!-- Shouts Count -->
|
||||
<span :style="{ opacity: post.shoutedCount ? 1 : .5 }">
|
||||
<ds-icon name="bullhorn" />
|
||||
<small>{{ post.shoutedCount }}</small>
|
||||
</span>
|
||||
|
||||
<!-- Comments Count -->
|
||||
<span :style="{ opacity: post.commentsCount ? 1 : .5 }">
|
||||
<ds-icon name="comments" />
|
||||
<small>{{ post.commentsCount }}</small>
|
||||
</span>
|
||||
<!-- Menu -->
|
||||
<content-menu
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:is-owner="isAuthor"
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Title -->
|
||||
<ds-heading tag="h3" no-margin>
|
||||
{{ post.title }}
|
||||
</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Post Content Excerpt -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<div class="hc-editor-content" v-html="excerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<!-- Footer o the Post -->
|
||||
<template slot="footer">
|
||||
<div style="display: inline-block; opacity: .5;">
|
||||
<!-- Categories -->
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{ content: category.name, placement: 'bottom-start', delay: { show: 500 } }"
|
||||
:icon="category.icon"
|
||||
/>
|
||||
</div>
|
||||
</no-ssr>
|
||||
</template>
|
||||
</ds-card>
|
||||
<no-ssr>
|
||||
<div style="display: inline-block; float: right">
|
||||
<!-- Shouts Count -->
|
||||
<span :style="{ opacity: post.shoutedCount ? 1 : 0.5 }">
|
||||
<ds-icon name="bullhorn" />
|
||||
<small>{{ post.shoutedCount }}</small>
|
||||
</span>
|
||||
|
||||
<!-- Comments Count -->
|
||||
<span :style="{ opacity: post.commentsCount ? 1 : 0.5 }">
|
||||
<ds-icon name="comments" />
|
||||
<small>{{ post.commentsCount }}</small>
|
||||
</span>
|
||||
<!-- Menu -->
|
||||
<content-menu
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:callbacks="{ confirm: deletePostCallback, cancel: null }"
|
||||
:is-owner="isAuthor"
|
||||
/>
|
||||
</div>
|
||||
</no-ssr>
|
||||
</template>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -82,6 +72,7 @@ import HcCategory from '~/components/Category'
|
||||
import HcRibbon from '~/components/Ribbon'
|
||||
// import { randomBytes } from 'crypto'
|
||||
import { mapGetters } from 'vuex'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
|
||||
export default {
|
||||
name: 'HcPostCard',
|
||||
@ -91,11 +82,16 @@ export default {
|
||||
HcRibbon,
|
||||
ContentMenu,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
props: {
|
||||
post: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
width: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
|
||||
@ -5,16 +5,12 @@
|
||||
role="search"
|
||||
:class="{
|
||||
'is-active': isActive,
|
||||
'is-open': isOpen
|
||||
'is-open': isOpen,
|
||||
}"
|
||||
>
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
<a
|
||||
v-if="isActive"
|
||||
class="search-clear-btn"
|
||||
@click="clear"
|
||||
>
|
||||
<a v-if="isActive" class="search-clear-btn" @click="clear">
|
||||
|
||||
</a>
|
||||
<ds-select
|
||||
@ -42,42 +38,31 @@
|
||||
@input.native="handleInput"
|
||||
@click.capture.native="isOpen = true"
|
||||
>
|
||||
<template
|
||||
slot="option"
|
||||
slot-scope="{option}"
|
||||
>
|
||||
<template slot="option" slot-scope="{ option }">
|
||||
<ds-flex>
|
||||
<ds-flex-item class="search-option-label">
|
||||
<ds-text>
|
||||
{{ option.label | truncate(70) }}
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item
|
||||
class="search-option-meta"
|
||||
width="280px"
|
||||
>
|
||||
<ds-flex-item class="search-option-meta" width="280px">
|
||||
<ds-flex>
|
||||
<ds-flex-item>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="softer"
|
||||
class="search-meta"
|
||||
>
|
||||
<ds-text size="small" color="softer" class="search-meta">
|
||||
<span style="text-align: right;">
|
||||
<b>{{ option.commentsCount }}</b> <ds-icon name="comments" />
|
||||
<b>{{ option.commentsCount }}</b>
|
||||
<ds-icon name="comments" />
|
||||
</span>
|
||||
<span style="width: 36px; display: inline-block; text-align: right;">
|
||||
<b>{{ option.shoutedCount }}</b> <ds-icon name="bullhorn" />
|
||||
<b>{{ option.shoutedCount }}</b>
|
||||
<ds-icon name="bullhorn" />
|
||||
</span>
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="softer"
|
||||
align="right"
|
||||
>
|
||||
{{ option.author.name | truncate(32) }} - {{ option.createdAt | dateTime('dd.MM.yyyy') }}
|
||||
<ds-text size="small" color="softer" align="right">
|
||||
{{ option.author.name | truncate(32) }} -
|
||||
{{ option.createdAt | dateTime('dd.MM.yyyy') }}
|
||||
</ds-text>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<ds-space
|
||||
margin="large"
|
||||
style="text-align: center"
|
||||
>
|
||||
<ds-space margin="large" style="text-align: center">
|
||||
<ds-button
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@ -13,16 +10,9 @@
|
||||
@click="toggle"
|
||||
/>
|
||||
<ds-space margin-bottom="xx-small" />
|
||||
<ds-text
|
||||
color="soft"
|
||||
class="shout-button-text"
|
||||
>
|
||||
<ds-heading
|
||||
style="display: inline"
|
||||
tag="h3"
|
||||
>
|
||||
{{ shoutedCount }}x
|
||||
</ds-heading> {{ $t('shoutButton.shouted') }}
|
||||
<ds-text color="soft" class="shout-button-text">
|
||||
<ds-heading style="display: inline" tag="h3">{{ shoutedCount }}x</ds-heading>
|
||||
{{ $t('shoutButton.shouted') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
</template>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
:include-styling="false"
|
||||
:style="backgroundImage"
|
||||
@vdropzone-thumbnail="thumbnail"
|
||||
@vdropzone-drop="vddrop"
|
||||
@vdropzone-error="verror"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -27,10 +27,11 @@ export default {
|
||||
return {
|
||||
dropzoneOptions: {
|
||||
url: this.vddrop,
|
||||
maxFilesize: 0.5,
|
||||
maxFilesize: 5.0,
|
||||
previewTemplate: this.template(),
|
||||
dictDefaultMessage: '',
|
||||
},
|
||||
error: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -44,6 +45,14 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
error() {
|
||||
let that = this
|
||||
setTimeout(function() {
|
||||
that.error = false
|
||||
}, 2000)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
template() {
|
||||
return `<div class="dz-preview dz-file-preview">
|
||||
@ -89,6 +98,12 @@ export default {
|
||||
})
|
||||
.catch(error => this.$toast.error(error.message))
|
||||
},
|
||||
verror(file, message) {
|
||||
if (file.status === 'error') {
|
||||
this.error = true
|
||||
this.$toast.error(file.status, message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -26,6 +26,7 @@ describe('Upload', () => {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$t: jest.fn(),
|
||||
}
|
||||
|
||||
const propsData = {
|
||||
@ -34,7 +35,7 @@ describe('Upload', () => {
|
||||
},
|
||||
}
|
||||
|
||||
const file = {
|
||||
const fileSuccess = {
|
||||
filename: 'avatar.jpg',
|
||||
previewElement: {
|
||||
classList: {
|
||||
@ -59,13 +60,38 @@ describe('Upload', () => {
|
||||
wrapper = shallowMount(Upload, { localVue, propsData, mocks })
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('sends a the UpdateUser mutation when vddrop is called', () => {
|
||||
wrapper.vm.vddrop([{ filename: 'avatar.jpg' }])
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('thumbnail', () => {
|
||||
wrapper.vm.thumbnail(file, dataUrl)
|
||||
expect(file.previewElement.classList.add).toHaveBeenCalledTimes(1)
|
||||
wrapper.vm.thumbnail(fileSuccess, dataUrl)
|
||||
expect(fileSuccess.previewElement.classList.add).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
describe('error handling', () => {
|
||||
const message = 'File upload failed'
|
||||
const fileError = { status: 'error' }
|
||||
|
||||
it('defaults to error false', () => {
|
||||
expect(wrapper.vm.error).toEqual(false)
|
||||
})
|
||||
|
||||
it('shows an error toaster when verror is called', () => {
|
||||
wrapper.vm.verror(fileError, message)
|
||||
expect(mocks.$toast.error).toHaveBeenCalledWith(fileError.status, message)
|
||||
})
|
||||
|
||||
it('changes error status from false to true to false', () => {
|
||||
wrapper.vm.verror(fileError, message)
|
||||
expect(wrapper.vm.error).toEqual(true)
|
||||
jest.runAllTimers()
|
||||
expect(wrapper.vm.error).toEqual(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -6,53 +6,24 @@
|
||||
<hc-avatar />
|
||||
</div>
|
||||
<div style="display: inline-block; height: 100%; vertical-align: middle;">
|
||||
<b
|
||||
class="username"
|
||||
style="vertical-align: middle;"
|
||||
>{{ $t('profile.userAnonym') }}</b>
|
||||
<b class="username" style="vertical-align: middle;">{{ $t('profile.userAnonym') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<dropdown
|
||||
v-else
|
||||
:class="{'disabled-content': user.disabled}"
|
||||
placement="top-start"
|
||||
offset="0"
|
||||
>
|
||||
<template
|
||||
slot="default"
|
||||
slot-scope="{openMenu, closeMenu, isOpen}"
|
||||
>
|
||||
<nuxt-link
|
||||
:to="userLink"
|
||||
:class="['user', isOpen && 'active']"
|
||||
>
|
||||
<div
|
||||
@mouseover="openMenu(true)"
|
||||
@mouseleave="closeMenu(true)"
|
||||
>
|
||||
<dropdown v-else :class="{ 'disabled-content': user.disabled }" placement="top-start" offset="0">
|
||||
<template slot="default" slot-scope="{ openMenu, closeMenu, isOpen }">
|
||||
<nuxt-link :to="userLink" :class="['user', isOpen && 'active']">
|
||||
<div @mouseover="openMenu(true)" @mouseleave="closeMenu(true)">
|
||||
<div
|
||||
style="display: inline-block; float: left; margin-right: 4px; height: 100%; vertical-align: middle;"
|
||||
>
|
||||
<hc-avatar
|
||||
:user="user"
|
||||
/>
|
||||
<hc-avatar :user="user" />
|
||||
</div>
|
||||
<div style="display: inline-block; height: 100%; vertical-align: middle;">
|
||||
<b
|
||||
class="username"
|
||||
style="vertical-align: middle;"
|
||||
>{{ userName | truncate(18) }}</b>
|
||||
<b class="username" style="vertical-align: middle;">{{ userName | truncate(18) }}</b>
|
||||
</div>
|
||||
<!-- Time -->
|
||||
<div
|
||||
v-if="dateTime"
|
||||
style="display: inline;"
|
||||
>
|
||||
<ds-text
|
||||
align="left"
|
||||
size="small"
|
||||
color="soft"
|
||||
>
|
||||
<div v-if="dateTime" style="display: inline;">
|
||||
<ds-text align="left" size="small" color="soft">
|
||||
<ds-icon name="clock" />
|
||||
<no-ssr>
|
||||
<hc-relative-date-time :date-time="dateTime" />
|
||||
@ -64,10 +35,7 @@
|
||||
</template>
|
||||
<template slot="popover">
|
||||
<div style="min-width: 250px">
|
||||
<hc-badges
|
||||
v-if="user.badges && user.badges.length"
|
||||
:badges="user.badges"
|
||||
/>
|
||||
<hc-badges v-if="user.badges && user.badges.length" :badges="user.badges" />
|
||||
<ds-text
|
||||
v-if="user.location"
|
||||
align="center"
|
||||
@ -82,11 +50,7 @@
|
||||
<ds-flex style="margin-top: -10px">
|
||||
<ds-flex-item class="ds-tab-nav-item">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="fanCount"
|
||||
:label="$t('profile.followers')"
|
||||
size="x-large"
|
||||
/>
|
||||
<ds-number :count="fanCount" :label="$t('profile.followers')" size="x-large" />
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item class="ds-tab-nav-item ds-tab-nav-item-active">
|
||||
@ -106,20 +70,16 @@
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
<ds-flex
|
||||
v-if="!itsMe"
|
||||
gutter="x-small"
|
||||
style="margin-bottom: 0;"
|
||||
>
|
||||
<ds-flex-item :width="{base: 3}">
|
||||
<ds-flex v-if="!itsMe" gutter="x-small" style="margin-bottom: 0;">
|
||||
<ds-flex-item :width="{ base: 3 }">
|
||||
<hc-follow-button
|
||||
:follow-id="user.id"
|
||||
:is-followed="user.followedByCurrentUser"
|
||||
@optimistic="follow => user.followedByCurrentUser = follow"
|
||||
@update="follow => user.followedByCurrentUser = follow"
|
||||
@optimistic="follow => (user.followedByCurrentUser = follow)"
|
||||
@update="follow => (user.followedByCurrentUser = follow)"
|
||||
/>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{base: 1}">
|
||||
<ds-flex-item :width="{ base: 1 }">
|
||||
<ds-button fullwidth>
|
||||
<ds-icon name="user-times" />
|
||||
</ds-button>
|
||||
|
||||
@ -1,36 +1,18 @@
|
||||
<template>
|
||||
<ds-form
|
||||
v-model="form"
|
||||
@submit="handleSubmit"
|
||||
>
|
||||
<ds-form v-model="form" @submit="handleSubmit">
|
||||
<template slot-scope="{ errors }">
|
||||
<ds-card>
|
||||
<hc-editor
|
||||
ref="editor"
|
||||
:users="users"
|
||||
:value="form.content"
|
||||
@input="updateEditorContent"
|
||||
/>
|
||||
<hc-editor ref="editor" :users="users" :value="form.content" @input="updateEditorContent" />
|
||||
<ds-space />
|
||||
<ds-flex :gutter="{ base: 'small', md: 'small', sm: 'x-large', xs: 'x-large' }">
|
||||
<ds-flex-item :width="{ base: '0%', md: '50%', sm: '0%', xs: '0%' }" />
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '30%', xs: '30%' }">
|
||||
<ds-button
|
||||
:disabled="disabled"
|
||||
ghost
|
||||
class="cancelBtn"
|
||||
@click.prevent="clear"
|
||||
>
|
||||
<ds-button :disabled="disabled" ghost class="cancelBtn" @click.prevent="clear">
|
||||
{{ $t('actions.cancel') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '40%', md: '20%', sm: '40%', xs: '40%' }">
|
||||
<ds-button
|
||||
type="submit"
|
||||
:loading="loading"
|
||||
:disabled="disabled || errors"
|
||||
primary
|
||||
>
|
||||
<ds-button type="submit" :loading="loading" :disabled="disabled || errors" primary>
|
||||
{{ $t('post.comment.submit') }}
|
||||
</ds-button>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -9,26 +9,22 @@
|
||||
color="primary"
|
||||
size="small"
|
||||
round
|
||||
>{{ comments.length }}</ds-tag> Comments
|
||||
>
|
||||
{{ comments.length }}
|
||||
</ds-tag>
|
||||
Comments
|
||||
</span>
|
||||
</h3>
|
||||
<ds-space margin-bottom="large" />
|
||||
<div
|
||||
v-if="comments && comments.length"
|
||||
id="comments"
|
||||
class="comments"
|
||||
>
|
||||
<div v-if="comments && comments.length" id="comments" class="comments">
|
||||
<comment
|
||||
v-for="comment in comments"
|
||||
v-for="(comment, index) in comments"
|
||||
:key="comment.id"
|
||||
:comment="comment"
|
||||
@deleteComment="comments.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
<hc-empty
|
||||
v-else
|
||||
name="empty"
|
||||
icon="messages"
|
||||
/>
|
||||
<hc-empty v-else name="empty" icon="messages" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
@ -1,18 +1,11 @@
|
||||
<template>
|
||||
<ds-space
|
||||
:class="{'notification': true, 'read': notification.read}"
|
||||
margin-bottom="x-small"
|
||||
>
|
||||
<ds-space :class="{ notification: true, read: notification.read }" margin-bottom="x-small">
|
||||
<no-ssr>
|
||||
<ds-space margin-bottom="x-small">
|
||||
<hc-user
|
||||
:user="post.author"
|
||||
:date-time="post.createdAt"
|
||||
:trunc="35"
|
||||
/>
|
||||
<hc-user :user="post.author" :date-time="post.createdAt" :trunc="35" />
|
||||
</ds-space>
|
||||
<ds-text color="soft">
|
||||
{{ $t("notifications.menu.mentioned") }}
|
||||
{{ $t('notifications.menu.mentioned') }}
|
||||
</ds-text>
|
||||
</no-ssr>
|
||||
<ds-space margin-bottom="x-small" />
|
||||
@ -22,16 +15,11 @@
|
||||
@click.native="$emit('read')"
|
||||
>
|
||||
<ds-space margin-bottom="x-small">
|
||||
<ds-card
|
||||
:header="post.title"
|
||||
:image="post.image"
|
||||
hover
|
||||
space="x-small"
|
||||
>
|
||||
<ds-card :header="post.title" :image="post.image" hover space="x-small">
|
||||
<ds-space margin-bottom="x-small" />
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<div v-html="excerpt" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
</ds-card>
|
||||
</ds-space>
|
||||
</nuxt-link>
|
||||
|
||||
@ -1,36 +1,16 @@
|
||||
<template>
|
||||
<ds-button
|
||||
v-if="totalNotifications <= 0"
|
||||
class="notifications-menu"
|
||||
disabled
|
||||
icon="bell"
|
||||
>
|
||||
<ds-button v-if="totalNotifications <= 0" class="notifications-menu" disabled icon="bell">
|
||||
{{ totalNotifications }}
|
||||
</ds-button>
|
||||
<dropdown
|
||||
v-else
|
||||
class="notifications-menu"
|
||||
>
|
||||
<template
|
||||
slot="default"
|
||||
slot-scope="{toggleMenu}"
|
||||
>
|
||||
<ds-button
|
||||
primary
|
||||
icon="bell"
|
||||
@click.prevent="toggleMenu"
|
||||
>
|
||||
<dropdown v-else class="notifications-menu">
|
||||
<template slot="default" slot-scope="{ toggleMenu }">
|
||||
<ds-button primary icon="bell" @click.prevent="toggleMenu">
|
||||
{{ totalNotifications }}
|
||||
</ds-button>
|
||||
</template>
|
||||
<template
|
||||
slot="popover"
|
||||
>
|
||||
<template slot="popover">
|
||||
<div class="notifications-menu-popover">
|
||||
<notification-list
|
||||
:notifications="notifications"
|
||||
@markAsRead="markAsRead"
|
||||
/>
|
||||
<notification-list :notifications="notifications" @markAsRead="markAsRead" />
|
||||
</div>
|
||||
</template>
|
||||
</dropdown>
|
||||
|
||||
@ -3,11 +3,7 @@
|
||||
<div class="main-navigation">
|
||||
<ds-container class="main-navigation-container">
|
||||
<div class="main-navigation-left">
|
||||
<a
|
||||
v-router-link
|
||||
style="display: inline-flex"
|
||||
href="/"
|
||||
>
|
||||
<a v-router-link style="display: inline-flex" href="/">
|
||||
<ds-logo />
|
||||
</a>
|
||||
</div>
|
||||
@ -24,11 +20,7 @@
|
||||
</div>
|
||||
<div class="main-navigation-right">
|
||||
<no-ssr>
|
||||
<locale-switch
|
||||
class="topbar-locale-switch"
|
||||
placement="bottom"
|
||||
offset="23"
|
||||
/>
|
||||
<locale-switch class="topbar-locale-switch" placement="bottom" offset="23" />
|
||||
</no-ssr>
|
||||
<template v-if="isLoggedIn">
|
||||
<no-ssr>
|
||||
@ -36,45 +28,32 @@
|
||||
</no-ssr>
|
||||
<no-ssr>
|
||||
<dropdown class="avatar-menu">
|
||||
<template
|
||||
slot="default"
|
||||
slot-scope="{toggleMenu}"
|
||||
>
|
||||
<template slot="default" slot-scope="{ toggleMenu }">
|
||||
<a
|
||||
class="avatar-menu-trigger"
|
||||
:href="$router.resolve({name: 'profile-id-slug', params: {id: user.id, slug: user.slug}}).href"
|
||||
:href="
|
||||
$router.resolve({
|
||||
name: 'profile-id-slug',
|
||||
params: { id: user.id, slug: user.slug },
|
||||
}).href
|
||||
"
|
||||
@click.prevent="toggleMenu"
|
||||
>
|
||||
<hc-avatar
|
||||
:user="user"
|
||||
/>
|
||||
<ds-icon
|
||||
size="xx-small"
|
||||
name="angle-down"
|
||||
/>
|
||||
<hc-avatar :user="user" />
|
||||
<ds-icon size="xx-small" name="angle-down" />
|
||||
</a>
|
||||
</template>
|
||||
<template
|
||||
slot="popover"
|
||||
slot-scope="{closeMenu}"
|
||||
>
|
||||
<template slot="popover" slot-scope="{ closeMenu }">
|
||||
<div class="avatar-menu-popover">
|
||||
{{ $t('login.hello') }}
|
||||
<b>{{ userName }}</b>
|
||||
<template v-if="user.role !== 'user'">
|
||||
<ds-text
|
||||
color="softer"
|
||||
size="small"
|
||||
style="margin-bottom: 0"
|
||||
>
|
||||
<ds-text color="softer" size="small" style="margin-bottom: 0">
|
||||
{{ user.role | camelCase }}
|
||||
</ds-text>
|
||||
</template>
|
||||
<hr>
|
||||
<ds-menu
|
||||
:routes="routes"
|
||||
:matcher="matcher"
|
||||
>
|
||||
<hr />
|
||||
<ds-menu :routes="routes" :matcher="matcher">
|
||||
<ds-menu-item
|
||||
slot="menuitem"
|
||||
slot-scope="item"
|
||||
@ -86,11 +65,8 @@
|
||||
{{ item.route.name }}
|
||||
</ds-menu-item>
|
||||
</ds-menu>
|
||||
<hr>
|
||||
<nuxt-link
|
||||
class="logout-link"
|
||||
:to="{ name: 'logout'}"
|
||||
>
|
||||
<hr />
|
||||
<nuxt-link class="logout-link" :to="{ name: 'logout' }">
|
||||
<ds-icon name="sign-out" />
|
||||
{{ $t('login.logout') }}
|
||||
</nuxt-link>
|
||||
|
||||
@ -135,19 +135,24 @@
|
||||
"takeAction": {
|
||||
"name": "Aktiv werden"
|
||||
},
|
||||
"delete": {
|
||||
"submit": "Löschen",
|
||||
"cancel": "Abbrechen",
|
||||
"success": "Beitrag erfolgreich gelöscht",
|
||||
"title": "Beitrag löschen",
|
||||
"type": "Beitrag",
|
||||
"message": "Möchtest Du wirklich den Beitrag \"<b>{name}</b>\" löschen?"
|
||||
"menu": {
|
||||
"edit": "Beitrag bearbeiten",
|
||||
"delete": "Beitrag löschen"
|
||||
},
|
||||
"comment": {
|
||||
"submit": "Kommentiere",
|
||||
"submitted": "Kommentar Gesendet"
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"content": {
|
||||
"unavailable-placeholder": "...dieser Kommentar ist nicht mehr verfügbar"
|
||||
},
|
||||
"menu": {
|
||||
"edit": "Kommentar bearbeiten",
|
||||
"delete": "Kommentar löschen"
|
||||
}
|
||||
},
|
||||
"quotes": {
|
||||
"african": {
|
||||
"quote": "Viele kleine Leute an vielen kleinen Orten, die viele kleine Dinge tun, werden das Antlitz dieser Welt verändern.",
|
||||
@ -210,6 +215,22 @@
|
||||
"message": "Bist du sicher, dass du den Kommentar \"<b>{name}</b>\" deaktivieren möchtest?"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"submit": "Löschen",
|
||||
"cancel": "Abbrechen",
|
||||
"contribution": {
|
||||
"title": "Lösche Beitrag",
|
||||
"type": "Contribution",
|
||||
"message": "Bist du sicher, dass du den Beitrag \"<b>{name}</b>\" löschen möchtest?",
|
||||
"success": "Beitrag erfolgreich gelöscht!"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Lösche Kommentar",
|
||||
"type": "Comment",
|
||||
"message": "Bist du sicher, dass du den Kommentar von \"<b>{name}</b>\" löschen möchtest?",
|
||||
"success": "Kommentar erfolgreich gelöscht!"
|
||||
}
|
||||
},
|
||||
"report": {
|
||||
"submit": "Melden",
|
||||
"cancel": "Abbrechen",
|
||||
@ -230,17 +251,6 @@
|
||||
"message": "Bist du sicher, dass du den Kommentar von \"<b>{name}</b>\" melden möchtest?"
|
||||
}
|
||||
},
|
||||
"contribution": {
|
||||
"edit": "Beitrag bearbeiten",
|
||||
"delete": "Beitrag löschen"
|
||||
},
|
||||
"comment": {
|
||||
"edit": "Kommentar bearbeiten",
|
||||
"delete": "Kommentar löschen",
|
||||
"content": {
|
||||
"unavailable-placeholder": "...dieser Kommentar ist nicht mehr verfügbar"
|
||||
}
|
||||
},
|
||||
"followButton": {
|
||||
"follow": "Folgen",
|
||||
"following": "Folge Ich"
|
||||
|
||||
@ -135,19 +135,24 @@
|
||||
"takeAction": {
|
||||
"name": "Take action"
|
||||
},
|
||||
"delete": {
|
||||
"submit": "Delete",
|
||||
"cancel": "Cancel",
|
||||
"success": "Post deleted successfully",
|
||||
"title": "Delete Post",
|
||||
"type": "Contribution",
|
||||
"message": "Do you really want to delete the post \"<b>{name}</b>\"?"
|
||||
"menu": {
|
||||
"edit": "Edit Post",
|
||||
"delete": "Delete Post"
|
||||
},
|
||||
"comment": {
|
||||
"submit": "Comment",
|
||||
"submitted": "Comment Submitted"
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"content": {
|
||||
"unavailable-placeholder": "...this comment is not available anymore"
|
||||
},
|
||||
"menu": {
|
||||
"edit": "Edit Comment",
|
||||
"delete": "Delete Comment"
|
||||
}
|
||||
},
|
||||
"quotes": {
|
||||
"african": {
|
||||
"quote": "Many small people in many small places do many small things, that can alter the face of the world.",
|
||||
@ -210,6 +215,22 @@
|
||||
"message": "Do you really want to disable the comment from \"<b>{name}</b>\"?"
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"submit": "Delete",
|
||||
"cancel": "Cancel",
|
||||
"contribution": {
|
||||
"title": "Delete Post",
|
||||
"type": "Contribution",
|
||||
"message": "Do you really want to delete the post \"<b>{name}</b>\"?",
|
||||
"success": "Post successfully deleted!"
|
||||
},
|
||||
"comment": {
|
||||
"title": "Delete Comment",
|
||||
"type": "Comment",
|
||||
"message": "Do you really want to delete the comment from \"<b>{name}</b>\"?",
|
||||
"success": "Comment successfully deleted!"
|
||||
}
|
||||
},
|
||||
"report": {
|
||||
"submit": "Report",
|
||||
"cancel": "Cancel",
|
||||
@ -230,17 +251,6 @@
|
||||
"message": "Do you really want to report the comment from \"<b>{name}</b>\"?"
|
||||
}
|
||||
},
|
||||
"contribution": {
|
||||
"edit": "Edit Contribution",
|
||||
"delete": "Delete Contribution"
|
||||
},
|
||||
"comment": {
|
||||
"edit": "Edit Comment",
|
||||
"delete": "Delete Comment",
|
||||
"content": {
|
||||
"unavailable-placeholder": "...this comment is not available anymore"
|
||||
}
|
||||
},
|
||||
"followButton": {
|
||||
"follow": "Follow",
|
||||
"following": "Following"
|
||||
|
||||
39
webapp/mixins/PostMutationHelpers.js
Normal file
39
webapp/mixins/PostMutationHelpers.js
Normal file
@ -0,0 +1,39 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async deletePostCallback(postDisplayType = 'list') {
|
||||
// console.log('inside "deletePostCallback" !!! ', this.post)
|
||||
try {
|
||||
var gqlMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
DeletePost(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
await this.$apollo.mutate({
|
||||
mutation: gqlMutation,
|
||||
variables: {
|
||||
id: this.post.id,
|
||||
},
|
||||
})
|
||||
this.$toast.success(this.$t('delete.contribution.success'))
|
||||
// console.log('called "this.$t" !!!')
|
||||
switch (postDisplayType) {
|
||||
case 'list':
|
||||
this.$emit('deletePost')
|
||||
// console.log('emitted "deletePost" !!!')
|
||||
break
|
||||
default:
|
||||
// case 'page'
|
||||
// console.log('called "this.$router.history.push" !!!')
|
||||
this.$router.history.push('/') // Single page type: Redirect to index (main) page
|
||||
break
|
||||
}
|
||||
} catch (err) {
|
||||
this.$toast.error(err.message)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -32,8 +32,8 @@ module.exports = {
|
||||
locales: require('./locales'),
|
||||
},
|
||||
/*
|
||||
** Headers of the page
|
||||
*/
|
||||
** Headers of the page
|
||||
*/
|
||||
head: {
|
||||
title: 'Human Connection',
|
||||
titleTemplate: '%s - Human Connection',
|
||||
@ -46,8 +46,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
/*
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: {
|
||||
color: '#86b31e',
|
||||
height: '2px',
|
||||
@ -55,20 +55,20 @@ module.exports = {
|
||||
},
|
||||
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
** Global CSS
|
||||
*/
|
||||
css: ['~assets/styles/main.scss'],
|
||||
|
||||
/*
|
||||
** Global processed styles
|
||||
*/
|
||||
** Global processed styles
|
||||
*/
|
||||
styleResources: {
|
||||
scss: styleguideStyles,
|
||||
},
|
||||
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [
|
||||
{
|
||||
src: `~/plugins/styleguide${process.env.STYLEGUIDE_DEV ? '-dev' : ''}.js`,
|
||||
@ -93,8 +93,8 @@ module.exports = {
|
||||
},
|
||||
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
modules: [
|
||||
['@nuxtjs/dotenv', { only: envWhitelist }],
|
||||
['nuxt-env', { keys: envWhitelist }],
|
||||
@ -105,8 +105,8 @@ module.exports = {
|
||||
],
|
||||
|
||||
/*
|
||||
** Axios module configuration
|
||||
*/
|
||||
** Axios module configuration
|
||||
*/
|
||||
axios: {
|
||||
// See https://github.com/nuxt-community/axios-module#options
|
||||
debug: dev,
|
||||
@ -181,12 +181,12 @@ module.exports = {
|
||||
},
|
||||
|
||||
/*
|
||||
** Build configuration
|
||||
*/
|
||||
** Build configuration
|
||||
*/
|
||||
build: {
|
||||
/*
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
extend(config, ctx) {
|
||||
if (process.env.STYLEGUIDE_DEV) {
|
||||
const path = require('path')
|
||||
|
||||
@ -29,7 +29,6 @@
|
||||
"!**/?(*.)+(spec|test).js?(x)"
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text",
|
||||
"lcov"
|
||||
],
|
||||
"transform": {
|
||||
@ -106,7 +105,7 @@
|
||||
"jest": "~24.8.0",
|
||||
"node-sass": "~4.12.0",
|
||||
"nodemon": "~1.19.1",
|
||||
"prettier": "~1.14.3",
|
||||
"prettier": "~1.17.1",
|
||||
"sass-loader": "~7.1.0",
|
||||
"tippy.js": "^4.3.1",
|
||||
"vue-jest": "~3.0.4",
|
||||
|
||||
@ -5,16 +5,10 @@
|
||||
</ds-heading>
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', md: '200px' }">
|
||||
<ds-menu
|
||||
:routes="routes"
|
||||
:is-exact="() => true"
|
||||
/>
|
||||
<ds-menu :routes="routes" :is-exact="() => true" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }">
|
||||
<transition
|
||||
name="slide-up"
|
||||
appear
|
||||
>
|
||||
<transition name="slide-up" appear>
|
||||
<nuxt-child />
|
||||
</transition>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.categories.name')">
|
||||
<ds-table
|
||||
:data="Category"
|
||||
:fields="fields"
|
||||
condensed
|
||||
>
|
||||
<template
|
||||
slot="icon"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<ds-table :data="Category" :fields="fields" condensed>
|
||||
<template slot="icon" slot-scope="scope">
|
||||
<ds-icon :name="scope.row.icon" />
|
||||
</template>
|
||||
</ds-table>
|
||||
|
||||
@ -5,34 +5,18 @@
|
||||
<ds-flex>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="0"
|
||||
:label="$t('admin.dashboard.users')"
|
||||
size="x-large"
|
||||
uppercase
|
||||
>
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.users')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countUsers || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countUsers || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="0"
|
||||
:label="$t('admin.dashboard.posts')"
|
||||
size="x-large"
|
||||
uppercase
|
||||
>
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.posts')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countPosts || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countPosts || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -46,10 +30,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countComments || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countComments || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -63,10 +44,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countNotifications || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countNotifications || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -80,10 +58,7 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countOrganizations || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countOrganizations || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
@ -97,61 +72,34 @@
|
||||
uppercase
|
||||
>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countProjects || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countProjects || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="0"
|
||||
:label="$t('admin.dashboard.invites')"
|
||||
size="x-large"
|
||||
uppercase
|
||||
>
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.invites')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countInvites || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countInvites || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="0"
|
||||
:label="$t('admin.dashboard.follows')"
|
||||
size="x-large"
|
||||
uppercase
|
||||
>
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.follows')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countFollows || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countFollows || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%', md: '33%' }">
|
||||
<ds-space margin="small">
|
||||
<ds-number
|
||||
:count="0"
|
||||
:label="$t('admin.dashboard.shouts')"
|
||||
size="x-large"
|
||||
uppercase
|
||||
>
|
||||
<ds-number :count="0" :label="$t('admin.dashboard.shouts')" size="x-large" uppercase>
|
||||
<no-ssr slot="count">
|
||||
<hc-count-to
|
||||
:start-val="0"
|
||||
:end-val="statistics.countShouts || 0"
|
||||
/>
|
||||
<hc-count-to :start-val="0" :end-val="statistics.countShouts || 0" />
|
||||
</no-ssr>
|
||||
</ds-number>
|
||||
</ds-space>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.notifications.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.organizations.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.pages.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.settings.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.tags.name')">
|
||||
<ds-table
|
||||
:data="Tag"
|
||||
:fields="fields"
|
||||
condensed
|
||||
>
|
||||
<template
|
||||
slot="id"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<ds-table :data="Tag" :fields="fields" condensed>
|
||||
<template slot="id" slot-scope="scope">
|
||||
{{ scope.index + 1 }}
|
||||
</template>
|
||||
</ds-table>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('admin.users.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,21 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<ds-flex
|
||||
v-if="Post && Post.length"
|
||||
:width="{ base: '100%' }"
|
||||
gutter="base"
|
||||
>
|
||||
<ds-flex-item
|
||||
v-for="post in uniq(Post)"
|
||||
<ds-flex v-if="Post && Post.length" :width="{ base: '100%' }" gutter="base">
|
||||
<hc-post-card
|
||||
v-for="(post, index) in uniq(Post)"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', xs: '100%', md: '50%', xl: '33%' }"
|
||||
>
|
||||
<hc-post-card :post="post" />
|
||||
</ds-flex-item>
|
||||
@deletePost="deletePost(index, post.id)"
|
||||
/>
|
||||
</ds-flex>
|
||||
<no-ssr>
|
||||
<ds-button
|
||||
v-tooltip="{content: 'Create a new Post', placement: 'left', delay: { show: 500 }}"
|
||||
v-tooltip="{ content: 'Create a new Post', placement: 'left', delay: { show: 500 } }"
|
||||
:path="{ name: 'post-create' }"
|
||||
class="post-add-button"
|
||||
icon="plus"
|
||||
@ -23,11 +19,7 @@
|
||||
primary
|
||||
/>
|
||||
</no-ssr>
|
||||
<hc-load-more
|
||||
v-if="true"
|
||||
:loading="$apollo.loading"
|
||||
@click="showMoreContributions"
|
||||
/>
|
||||
<hc-load-more v-if="true" :loading="$apollo.loading" @click="showMoreContributions" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -86,6 +78,14 @@ export default {
|
||||
fetchPolicy: 'cache-and-network',
|
||||
})
|
||||
},
|
||||
deletePost(_index, postId) {
|
||||
this.Post = this.Post.filter(post => {
|
||||
return post.id !== postId
|
||||
})
|
||||
// Why "uniq(Post)" is used in the array for list creation?
|
||||
// Ideal solution here:
|
||||
// this.Post.splice(index, 1)
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
Post: {
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
<template>
|
||||
<transition
|
||||
name="fade"
|
||||
appear
|
||||
>
|
||||
<ds-container
|
||||
v-if="ready"
|
||||
width="small"
|
||||
>
|
||||
<transition name="fade" appear>
|
||||
<ds-container v-if="ready" width="small">
|
||||
<ds-space margin="small">
|
||||
<blockquote>
|
||||
<p>{{ $t('quotes.african.quote') }}</p>
|
||||
@ -15,41 +9,25 @@
|
||||
</ds-space>
|
||||
<ds-card class="login-card">
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item
|
||||
:width="{ base: '100%', sm: '50%' }"
|
||||
centered
|
||||
>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<no-ssr>
|
||||
<locale-switch
|
||||
class="login-locale-switch"
|
||||
offset="5"
|
||||
/>
|
||||
<locale-switch class="login-locale-switch" offset="5" />
|
||||
</no-ssr>
|
||||
<ds-space
|
||||
margin-top="small"
|
||||
margin-bottom="xxx-small"
|
||||
centered
|
||||
>
|
||||
<ds-space margin-top="small" margin-bottom="xxx-small" centered>
|
||||
<img
|
||||
class="login-image"
|
||||
alt="Human Connection"
|
||||
src="/img/sign-up/humanconnection.svg"
|
||||
>
|
||||
/>
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item
|
||||
:width="{ base: '100%', sm: '50%' }"
|
||||
centered
|
||||
>
|
||||
<ds-flex-item :width="{ base: '100%', sm: '50%' }" centered>
|
||||
<ds-space margin="small">
|
||||
<ds-text size="small">
|
||||
{{ $t('login.copy') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<form
|
||||
:disabled="pending"
|
||||
@submit.prevent="onSubmit"
|
||||
>
|
||||
<form :disabled="pending" @submit.prevent="onSubmit">
|
||||
<ds-input
|
||||
v-model="form.email"
|
||||
:disabled="pending"
|
||||
@ -82,7 +60,9 @@
|
||||
:href="$t('login.moreInfoURL')"
|
||||
:title="$t('login.moreInfoHint')"
|
||||
target="_blank"
|
||||
>{{ $t('login.moreInfo') }}</a>
|
||||
>
|
||||
{{ $t('login.moreInfo') }}
|
||||
</a>
|
||||
</ds-space>
|
||||
</form>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -1,32 +1,12 @@
|
||||
<template>
|
||||
<ds-container width="small">
|
||||
<ds-flex>
|
||||
<ds-flex-item
|
||||
:width="{ base: '100%' }"
|
||||
centered
|
||||
>
|
||||
<ds-space
|
||||
style="text-align: center;"
|
||||
margin-top="large"
|
||||
margin-bottom="xxx-small"
|
||||
centered
|
||||
>
|
||||
<img
|
||||
style="width: 200px;"
|
||||
src="/img/sign-up/onourjourney.png"
|
||||
alt="Human Connection"
|
||||
>
|
||||
<ds-flex-item :width="{ base: '100%' }" centered>
|
||||
<ds-space style="text-align: center;" margin-top="large" margin-bottom="xxx-small" centered>
|
||||
<img style="width: 200px;" src="/img/sign-up/onourjourney.png" alt="Human Connection" />
|
||||
</ds-space>
|
||||
<ds-space
|
||||
style="text-align: center;"
|
||||
margin-top="small"
|
||||
margin-bottom="xxx-small"
|
||||
centered
|
||||
>
|
||||
<ds-heading
|
||||
tag="h3"
|
||||
soft
|
||||
>
|
||||
<ds-space style="text-align: center;" margin-top="small" margin-bottom="xxx-small" centered>
|
||||
<ds-heading tag="h3" soft>
|
||||
Logging out...
|
||||
</ds-heading>
|
||||
</ds-space>
|
||||
|
||||
@ -8,10 +8,7 @@
|
||||
<ds-menu :routes="routes" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }">
|
||||
<transition
|
||||
name="slide-up"
|
||||
appear
|
||||
>
|
||||
<transition name="slide-up" appear>
|
||||
<nuxt-child />
|
||||
</transition>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -3,56 +3,48 @@
|
||||
<ds-heading tag="h3">
|
||||
{{ $t('moderation.reports.name') }}
|
||||
</ds-heading>
|
||||
<ds-table
|
||||
v-if="Report && Report.length"
|
||||
:data="Report"
|
||||
:fields="fields"
|
||||
condensed
|
||||
>
|
||||
<template
|
||||
slot="name"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<ds-table v-if="Report && Report.length" :data="Report" :fields="fields" condensed>
|
||||
<template slot="name" slot-scope="scope">
|
||||
<div v-if="scope.row.type === 'Post'">
|
||||
<nuxt-link
|
||||
:to="{ name: 'post-id-slug', params: { id: scope.row.post.id, slug: scope.row.post.slug } }"
|
||||
:to="{
|
||||
name: 'post-id-slug',
|
||||
params: { id: scope.row.post.id, slug: scope.row.post.slug },
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.post.title | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
<br>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="soft"
|
||||
>
|
||||
<br />
|
||||
<ds-text size="small" color="soft">
|
||||
{{ scope.row.post.author.name }}
|
||||
</ds-text>
|
||||
</div>
|
||||
<div v-else-if="scope.row.type === 'Comment'">
|
||||
<nuxt-link
|
||||
:to="{ name: 'post-id-slug', params: { id: scope.row.comment.post.id, slug: scope.row.comment.post.slug } }"
|
||||
:to="{
|
||||
name: 'post-id-slug',
|
||||
params: { id: scope.row.comment.post.id, slug: scope.row.comment.post.slug },
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.comment.contentExcerpt | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
<br>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="soft"
|
||||
>
|
||||
<br />
|
||||
<ds-text size="small" color="soft">
|
||||
{{ scope.row.comment.author.name }}
|
||||
</ds-text>
|
||||
</div>
|
||||
<div v-else>
|
||||
<nuxt-link
|
||||
:to="{ name: 'profile-id-slug', params: { id: scope.row.user.id, slug: scope.row.user.slug } }"
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.user.id, slug: scope.row.user.slug },
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.user.name | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="type"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<template slot="type" slot-scope="scope">
|
||||
<ds-text color="soft">
|
||||
<ds-icon
|
||||
v-if="scope.row.type === 'Post'"
|
||||
@ -71,45 +63,50 @@
|
||||
/>
|
||||
</ds-text>
|
||||
</template>
|
||||
<template
|
||||
slot="submitter"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<template slot="submitter" slot-scope="scope">
|
||||
<nuxt-link
|
||||
:to="{ name: 'profile-id-slug', params: { id: scope.row.submitter.id, slug: scope.row.submitter.slug } }"
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.submitter.id, slug: scope.row.submitter.slug },
|
||||
}"
|
||||
>
|
||||
{{ scope.row.submitter.name }}
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<template
|
||||
slot="disabledBy"
|
||||
slot-scope="scope"
|
||||
>
|
||||
<template slot="disabledBy" slot-scope="scope">
|
||||
<nuxt-link
|
||||
v-if="scope.row.type === 'Post' && scope.row.post.disabledBy"
|
||||
:to="{ name: 'profile-id-slug', params: { id: scope.row.post.disabledBy.id, slug: scope.row.post.disabledBy.slug } }"
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.post.disabledBy.id, slug: scope.row.post.disabledBy.slug },
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.post.disabledBy.name | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-else-if="scope.row.type === 'Comment' && scope.row.comment.disabledBy"
|
||||
:to="{ name: 'profile-id-slug', params: { id: scope.row.comment.disabledBy.id, slug: scope.row.comment.disabledBy.slug } }"
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: {
|
||||
id: scope.row.comment.disabledBy.id,
|
||||
slug: scope.row.comment.disabledBy.slug,
|
||||
},
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.comment.disabledBy.name | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
<nuxt-link
|
||||
v-else-if="scope.row.type === 'User' && scope.row.user.disabledBy"
|
||||
:to="{ name: 'profile-id-slug', params: { id: scope.row.user.disabledBy.id, slug: scope.row.user.disabledBy.slug } }"
|
||||
:to="{
|
||||
name: 'profile-id-slug',
|
||||
params: { id: scope.row.user.disabledBy.id, slug: scope.row.user.disabledBy.slug },
|
||||
}"
|
||||
>
|
||||
<b>{{ scope.row.user.disabledBy.name | truncate(50) }}</b>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
</ds-table>
|
||||
<hc-empty
|
||||
v-else
|
||||
icon="alert"
|
||||
:message="$t('moderation.reports.empty')"
|
||||
/>
|
||||
<hc-empty v-else icon="alert" :message="$t('moderation.reports.empty')" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -2,18 +2,12 @@
|
||||
<div>
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', sm: 2, md: 2, lg: 1 }">
|
||||
<transition
|
||||
name="slide-up"
|
||||
appear
|
||||
>
|
||||
<transition name="slide-up" appear>
|
||||
<nuxt-child />
|
||||
</transition>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '200px' }">
|
||||
<ds-menu
|
||||
:routes="routes"
|
||||
class="post-side-navigation"
|
||||
/>
|
||||
<ds-menu :routes="routes" class="post-side-navigation" />
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</div>
|
||||
|
||||
100
webapp/pages/post/_id/_slug/index.spec.js
Normal file
100
webapp/pages/post/_id/_slug/index.spec.js
Normal file
@ -0,0 +1,100 @@
|
||||
import { config, shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import PostSlug from './index.vue'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
|
||||
config.stubs['no-ssr'] = '<span><slot /></span>'
|
||||
|
||||
describe('PostSlug', () => {
|
||||
let wrapper
|
||||
let Wrapper
|
||||
let store
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
store = new Vuex.Store({
|
||||
getters: {
|
||||
'auth/user': () => {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
})
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$filters: {
|
||||
truncate: a => a,
|
||||
},
|
||||
// If you mocking router, than don't use VueRouter with lacalVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
|
||||
$router: {
|
||||
history: {
|
||||
push: jest.fn(),
|
||||
},
|
||||
},
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
Wrapper = () => {
|
||||
return shallowMount(PostSlug, {
|
||||
store,
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test mixin "PostMutationHelpers"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
post: {
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
author: {
|
||||
id: 'u1',
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('deletion of Post from Page by invoking "deletePostCallback(`page`)"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deletePostCallback('page')
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('not emits "deletePost"', () => {
|
||||
expect(wrapper.emitted().deletePost).toBeFalsy()
|
||||
})
|
||||
|
||||
it('does go to index (main) page', () => {
|
||||
expect(mocks.$router.history.push).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,41 +1,30 @@
|
||||
<template>
|
||||
<transition
|
||||
name="fade"
|
||||
appear
|
||||
>
|
||||
<transition name="fade" appear>
|
||||
<ds-card
|
||||
v-if="post && ready"
|
||||
:image="post.image"
|
||||
:class="{'post-card': true, 'disabled-content': post.disabled}"
|
||||
:class="{ 'post-card': true, 'disabled-content': post.disabled }"
|
||||
>
|
||||
<ds-space margin-bottom="small" />
|
||||
<hc-user
|
||||
:user="post.author"
|
||||
:date-time="post.createdAt"
|
||||
/>
|
||||
<hc-user :user="post.author" :date-time="post.createdAt" />
|
||||
<no-ssr>
|
||||
<content-menu
|
||||
placement="bottom-end"
|
||||
resource-type="contribution"
|
||||
:resource="post"
|
||||
:callbacks="{ confirm: () => deletePostCallback('page'), cancel: null }"
|
||||
:is-owner="isAuthor(post.author.id)"
|
||||
/>
|
||||
</no-ssr>
|
||||
<ds-space margin-bottom="small" />
|
||||
<ds-heading
|
||||
tag="h3"
|
||||
no-margin
|
||||
>
|
||||
<ds-heading tag="h3" no-margin>
|
||||
{{ post.title }}
|
||||
</ds-heading>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Content -->
|
||||
<!-- eslint-disable vue/no-v-html -->
|
||||
<!-- TODO: replace editor content with tiptap render view -->
|
||||
<div
|
||||
class="content hc-editor-content"
|
||||
v-html="post.content"
|
||||
/>
|
||||
<div class="content hc-editor-content" v-html="post.content" />
|
||||
<!-- eslint-enable vue/no-v-html -->
|
||||
<ds-space margin="xx-large" />
|
||||
<!-- Categories -->
|
||||
@ -44,23 +33,16 @@
|
||||
<hc-category
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{content: category.name, placement: 'top-start', delay: { show: 300 }}"
|
||||
v-tooltip="{ content: category.name, placement: 'top-start', delay: { show: 300 } }"
|
||||
:icon="category.icon"
|
||||
:name="category.name"
|
||||
/>
|
||||
</div>
|
||||
<ds-space margin-bottom="small" />
|
||||
<!-- Tags -->
|
||||
<div
|
||||
v-if="post.tags && post.tags.length"
|
||||
class="tags"
|
||||
>
|
||||
<div v-if="post.tags && post.tags.length" class="tags">
|
||||
<ds-space margin="xx-small" />
|
||||
<hc-tag
|
||||
v-for="tag in post.tags"
|
||||
:key="tag.id"
|
||||
:name="tag.name"
|
||||
/>
|
||||
<hc-tag v-for="tag in post.tags" :key="tag.id" :name="tag.name" />
|
||||
</div>
|
||||
<!-- Shout Button -->
|
||||
<hc-shout-button
|
||||
@ -90,8 +72,10 @@ import HcUser from '~/components/User'
|
||||
import HcShoutButton from '~/components/ShoutButton.vue'
|
||||
import HcCommentForm from '~/components/comments/CommentForm'
|
||||
import HcCommentList from '~/components/comments/CommentList'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
|
||||
export default {
|
||||
name: 'PostSlug',
|
||||
transition: {
|
||||
name: 'slide-up',
|
||||
mode: 'out-in',
|
||||
@ -105,6 +89,7 @@ export default {
|
||||
HcCommentForm,
|
||||
HcCommentList,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
head() {
|
||||
return {
|
||||
title: this.title,
|
||||
|
||||
@ -5,50 +5,47 @@
|
||||
</h2>
|
||||
<p>Hier findest du weitere infos zum Thema.</p>
|
||||
<ds-space />
|
||||
<h3><ds-icon name="compass" /> Themenkategorien</h3>
|
||||
<h3>
|
||||
<ds-icon name="compass" />
|
||||
Themenkategorien
|
||||
</h3>
|
||||
<div class="tags">
|
||||
<ds-icon
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"
|
||||
v-tooltip="{content: category.name, placement: 'top-start', delay: { show: 300 }}"
|
||||
v-tooltip="{ content: category.name, placement: 'top-start', delay: { show: 300 } }"
|
||||
:name="category.icon"
|
||||
size="large"
|
||||
/>
|
||||
/>
|
||||
|
||||
<!--<ds-tag
|
||||
v-for="category in post.categories"
|
||||
:key="category.id"><ds-icon :name="category.icon" /> {{ category.name }}</ds-tag>-->
|
||||
:key="category.id"><ds-icon :name="category.icon" /> {{ category.name }}</ds-tag>-->
|
||||
</div>
|
||||
<template v-if="post.tags && post.tags.length">
|
||||
<h3><ds-icon name="tags" /> Schlagwörter</h3>
|
||||
<h3>
|
||||
<ds-icon name="tags" />
|
||||
Schlagwörter
|
||||
</h3>
|
||||
<div class="tags">
|
||||
<ds-tag
|
||||
v-for="tag in post.tags"
|
||||
:key="tag.id"
|
||||
>
|
||||
<ds-icon name="tag" /> {{ tag.name }}
|
||||
<ds-tag v-for="tag in post.tags" :key="tag.id">
|
||||
<ds-icon name="tag" />
|
||||
{{ tag.name }}
|
||||
</ds-tag>
|
||||
</div>
|
||||
</template>
|
||||
<h3>Verwandte Beiträge</h3>
|
||||
<ds-section style="margin: 0 -1.5rem; padding: 1.5rem;">
|
||||
<ds-flex
|
||||
v-if="post.relatedContributions && post.relatedContributions.length"
|
||||
gutter="small"
|
||||
>
|
||||
<ds-flex-item
|
||||
v-for="relatedPost in post.relatedContributions"
|
||||
<ds-flex v-if="post.relatedContributions && post.relatedContributions.length" gutter="small">
|
||||
<hc-post-card
|
||||
v-for="(relatedPost, index) in post.relatedContributions"
|
||||
:key="relatedPost.id"
|
||||
:post="relatedPost"
|
||||
:width="{ base: '100%', lg: 1 }"
|
||||
>
|
||||
<hc-post-card :post="relatedPost" />
|
||||
</ds-flex-item>
|
||||
@deletePost="post.relatedContributions.splice(index, 1)"
|
||||
/>
|
||||
</ds-flex>
|
||||
<hc-empty
|
||||
v-else
|
||||
margin="large"
|
||||
icon="file"
|
||||
message="No related Posts"
|
||||
/>
|
||||
<hc-empty v-else margin="large" icon="file" message="No related Posts" />
|
||||
</ds-section>
|
||||
<ds-space margin-bottom="large" />
|
||||
</ds-card>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card header="Werde aktiv!">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<ds-flex
|
||||
:width="{ base: '100%' }"
|
||||
gutter="base"
|
||||
>
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', md: 3 }">
|
||||
<hc-contribution-form />
|
||||
</ds-flex-item>
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<ds-flex
|
||||
:width="{ base: '100%' }"
|
||||
gutter="base"
|
||||
>
|
||||
<ds-flex :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', md: 3 }">
|
||||
<hc-contribution-form :contribution="contribution" />
|
||||
</ds-flex-item>
|
||||
|
||||
81
webapp/pages/profile/_id/_slug.spec.js
Normal file
81
webapp/pages/profile/_id/_slug.spec.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils'
|
||||
import ProfileSlug from './_slug.vue'
|
||||
import Vuex from 'vuex'
|
||||
import Styleguide from '@human-connection/styleguide'
|
||||
|
||||
const localVue = createLocalVue()
|
||||
|
||||
localVue.use(Vuex)
|
||||
localVue.use(Styleguide)
|
||||
|
||||
describe('ProfileSlug', () => {
|
||||
let wrapper
|
||||
let Wrapper
|
||||
let mocks
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
post: {
|
||||
id: 'p23',
|
||||
name: 'It is a post',
|
||||
},
|
||||
$t: jest.fn(),
|
||||
// If you mocking router, than don't use VueRouter with lacalVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
|
||||
$router: {
|
||||
history: {
|
||||
push: jest.fn(),
|
||||
},
|
||||
},
|
||||
$toast: {
|
||||
success: jest.fn(),
|
||||
error: jest.fn(),
|
||||
},
|
||||
$apollo: {
|
||||
mutate: jest.fn().mockResolvedValue(),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
describe('shallowMount', () => {
|
||||
Wrapper = () => {
|
||||
return shallowMount(ProfileSlug, {
|
||||
mocks,
|
||||
localVue,
|
||||
})
|
||||
}
|
||||
|
||||
beforeEach(jest.useFakeTimers)
|
||||
|
||||
describe('test mixin "PostMutationHelpers"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('deletion of Post from List by invoking "deletePostCallback(`list`)"', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.vm.deletePostCallback('list')
|
||||
})
|
||||
|
||||
describe('after timeout', () => {
|
||||
beforeEach(jest.runAllTimers)
|
||||
|
||||
it('emits "deletePost"', () => {
|
||||
expect(wrapper.emitted().deletePost.length).toBe(1)
|
||||
})
|
||||
|
||||
it('does not go to index (main) page', () => {
|
||||
expect(mocks.$router.history.push).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does call mutation', () => {
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('mutation is successful', () => {
|
||||
expect(mocks.$toast.success).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -4,84 +4,49 @@
|
||||
<p>PROFILE IMAGE</p>
|
||||
</ds-card>
|
||||
<ds-space />
|
||||
<ds-flex
|
||||
v-if="user"
|
||||
:width="{ base: '100%' }"
|
||||
gutter="base"
|
||||
>
|
||||
<ds-flex v-if="user" :width="{ base: '100%' }" gutter="base">
|
||||
<ds-flex-item :width="{ base: '100%', sm: 2, md: 2, lg: 1 }">
|
||||
<ds-card
|
||||
:class="{'disabled-content': user.disabled}"
|
||||
:class="{ 'disabled-content': user.disabled }"
|
||||
style="position: relative; height: auto;"
|
||||
>
|
||||
<hc-upload
|
||||
v-if="myProfile"
|
||||
:user="user"
|
||||
/>
|
||||
<hc-avatar
|
||||
v-else
|
||||
:user="user"
|
||||
class="profile-avatar"
|
||||
size="x-large"
|
||||
/>
|
||||
<hc-upload v-if="myProfile" :user="user" />
|
||||
<hc-avatar v-else :user="user" class="profile-avatar" size="x-large" />
|
||||
<no-ssr>
|
||||
<content-menu
|
||||
placement="bottom-end"
|
||||
resource-type="user"
|
||||
:resource="user"
|
||||
:callbacks="{ confirm: deletePostCallback, cancel: null }"
|
||||
:is-owner="myProfile"
|
||||
class="user-content-menu"
|
||||
/>
|
||||
</no-ssr>
|
||||
<ds-space margin="small">
|
||||
<ds-heading
|
||||
tag="h3"
|
||||
align="center"
|
||||
no-margin
|
||||
>
|
||||
{{ userName }}
|
||||
</ds-heading>
|
||||
<ds-text
|
||||
v-if="user.location"
|
||||
align="center"
|
||||
color="soft"
|
||||
size="small"
|
||||
>
|
||||
<ds-heading tag="h3" align="center" no-margin>{{ userName }}</ds-heading>
|
||||
<ds-text v-if="user.location" align="center" color="soft" size="small">
|
||||
<ds-icon name="map-marker" />
|
||||
{{ user.location.name }}
|
||||
</ds-text>
|
||||
<ds-text
|
||||
align="center"
|
||||
color="soft"
|
||||
size="small"
|
||||
>
|
||||
<ds-text align="center" color="soft" size="small">
|
||||
{{ $t('profile.memberSince') }} {{ user.createdAt | date('MMMM yyyy') }}
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
<ds-space
|
||||
v-if="user.badges && user.badges.length"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-if="user.badges && user.badges.length" margin="x-small">
|
||||
<hc-badges :badges="user.badges" />
|
||||
</ds-space>
|
||||
<ds-flex>
|
||||
<ds-flex-item>
|
||||
<no-ssr>
|
||||
<ds-number :label="$t('profile.followers')">
|
||||
<hc-count-to
|
||||
slot="count"
|
||||
:end-val="followedByCount"
|
||||
/>
|
||||
<hc-count-to slot="count" :end-val="followedByCount" />
|
||||
</ds-number>
|
||||
</no-ssr>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item>
|
||||
<no-ssr>
|
||||
<ds-number :label="$t('profile.following')">
|
||||
<hc-count-to
|
||||
slot="count"
|
||||
:end-val="Number(user.followingCount) || 0"
|
||||
/>
|
||||
<hc-count-to slot="count" :end-val="Number(user.followingCount) || 0" />
|
||||
</ds-number>
|
||||
</no-ssr>
|
||||
</ds-flex-item>
|
||||
@ -91,140 +56,72 @@
|
||||
v-if="!myProfile"
|
||||
:follow-id="user.id"
|
||||
:is-followed="user.followedByCurrentUser"
|
||||
@optimistic="follow => user.followedByCurrentUser = follow"
|
||||
@optimistic="follow => (user.followedByCurrentUser = follow)"
|
||||
@update="follow => fetchUser()"
|
||||
/>
|
||||
</ds-space>
|
||||
<template v-if="user.about">
|
||||
<hr>
|
||||
<ds-space
|
||||
margin-top="small"
|
||||
margin-bottom="small"
|
||||
>
|
||||
<ds-text
|
||||
color="soft"
|
||||
size="small"
|
||||
>
|
||||
{{ user.about }}
|
||||
</ds-text>
|
||||
<hr />
|
||||
<ds-space margin-top="small" margin-bottom="small">
|
||||
<ds-text color="soft" size="small">{{ user.about }}</ds-text>
|
||||
</ds-space>
|
||||
</template>
|
||||
</ds-card>
|
||||
<ds-space />
|
||||
<ds-heading
|
||||
tag="h3"
|
||||
soft
|
||||
style="text-align: center; margin-bottom: 10px;"
|
||||
>
|
||||
<ds-heading tag="h3" soft style="text-align: center; margin-bottom: 10px;">
|
||||
Netzwerk
|
||||
</ds-heading>
|
||||
<ds-card style="position: relative; height: auto;">
|
||||
<ds-space
|
||||
v-if="user.following && user.following.length"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-text
|
||||
tag="h5"
|
||||
color="soft"
|
||||
>
|
||||
Wem folgt {{ userName | truncate(15) }}?
|
||||
</ds-text>
|
||||
<ds-space v-if="user.following && user.following.length" margin="x-small">
|
||||
<ds-text tag="h5" color="soft">Wem folgt {{ userName | truncate(15) }}?</ds-text>
|
||||
</ds-space>
|
||||
<template v-if="user.following && user.following.length">
|
||||
<ds-space
|
||||
v-for="follow in uniq(user.following)"
|
||||
:key="follow.id"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-for="follow in uniq(user.following)" :key="follow.id" margin="x-small">
|
||||
<!-- TODO: find better solution for rendering errors -->
|
||||
<no-ssr>
|
||||
<user
|
||||
:user="follow"
|
||||
:trunc="15"
|
||||
/>
|
||||
<user :user="follow" :trunc="15" />
|
||||
</no-ssr>
|
||||
</ds-space>
|
||||
<ds-space
|
||||
v-if="user.followingCount - user.following.length"
|
||||
margin="small"
|
||||
>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="softer"
|
||||
>
|
||||
<ds-space v-if="user.followingCount - user.following.length" margin="small">
|
||||
<ds-text size="small" color="softer">
|
||||
und {{ user.followingCount - user.following.length }} weitere
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p style="text-align: center; opacity: .5;">
|
||||
{{ userName }} folgt niemandem
|
||||
</p>
|
||||
<p style="text-align: center; opacity: .5;">{{ userName }} folgt niemandem</p>
|
||||
</template>
|
||||
</ds-card>
|
||||
<ds-space />
|
||||
<ds-card style="position: relative; height: auto;">
|
||||
<ds-space
|
||||
v-if="user.followedBy && user.followedBy.length"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-text
|
||||
tag="h5"
|
||||
color="soft"
|
||||
>
|
||||
Wer folgt {{ userName | truncate(15) }}?
|
||||
</ds-text>
|
||||
<ds-space v-if="user.followedBy && user.followedBy.length" margin="x-small">
|
||||
<ds-text tag="h5" color="soft">Wer folgt {{ userName | truncate(15) }}?</ds-text>
|
||||
</ds-space>
|
||||
<template v-if="user.followedBy && user.followedBy.length">
|
||||
<ds-space
|
||||
v-for="follow in uniq(user.followedBy)"
|
||||
:key="follow.id"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-for="follow in uniq(user.followedBy)" :key="follow.id" margin="x-small">
|
||||
<!-- TODO: find better solution for rendering errors -->
|
||||
<no-ssr>
|
||||
<user
|
||||
:user="follow"
|
||||
:trunc="15"
|
||||
/>
|
||||
<user :user="follow" :trunc="15" />
|
||||
</no-ssr>
|
||||
</ds-space>
|
||||
<ds-space
|
||||
v-if="user.followedByCount - user.followedBy.length"
|
||||
margin="small"
|
||||
>
|
||||
<ds-text
|
||||
size="small"
|
||||
color="softer"
|
||||
>
|
||||
<ds-space v-if="user.followedByCount - user.followedBy.length" margin="small">
|
||||
<ds-text size="small" color="softer">
|
||||
und {{ user.followedByCount - user.followedBy.length }} weitere
|
||||
</ds-text>
|
||||
</ds-space>
|
||||
</template>
|
||||
<template v-else>
|
||||
<p style="text-align: center; opacity: .5;">
|
||||
niemand folgt {{ userName }}
|
||||
</p>
|
||||
<p style="text-align: center; opacity: .5;">niemand folgt {{ userName }}</p>
|
||||
</template>
|
||||
</ds-card>
|
||||
<ds-space
|
||||
v-if="user.socialMedia && user.socialMedia.length"
|
||||
margin="large"
|
||||
>
|
||||
<ds-space v-if="user.socialMedia && user.socialMedia.length" margin="large">
|
||||
<ds-card style="position: relative; height: auto;">
|
||||
<ds-space margin="x-small">
|
||||
<ds-text
|
||||
tag="h5"
|
||||
color="soft"
|
||||
>
|
||||
<ds-text tag="h5" color="soft">
|
||||
{{ $t('profile.socialMedia') }} {{ user.name | truncate(15) }}?
|
||||
</ds-text>
|
||||
<template>
|
||||
<ds-space
|
||||
v-for="link in socialMediaLinks"
|
||||
:key="link.username"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-for="link in socialMediaLinks" :key="link.username" margin="x-small">
|
||||
<a :href="link.url">
|
||||
<ds-avatar :image="link.favicon" />
|
||||
{{ 'link.username' }}
|
||||
@ -236,10 +133,7 @@
|
||||
</ds-space>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
|
||||
<ds-flex
|
||||
:width="{ base: '100%' }"
|
||||
gutter="small"
|
||||
>
|
||||
<ds-flex :width="{ base: '100%' }" gutter="small">
|
||||
<ds-flex-item class="profile-top-navigation">
|
||||
<ds-card class="ds-tab-nav">
|
||||
<ds-flex>
|
||||
@ -248,10 +142,7 @@
|
||||
<!-- TODO: find better solution for rendering errors -->
|
||||
<no-ssr>
|
||||
<ds-number :label="$t('common.post', null, user.contributionsCount)">
|
||||
<hc-count-to
|
||||
slot="count"
|
||||
:end-val="user.contributionsCount"
|
||||
/>
|
||||
<hc-count-to slot="count" :end-val="user.contributionsCount" />
|
||||
</ds-number>
|
||||
</no-ssr>
|
||||
</ds-space>
|
||||
@ -284,7 +175,7 @@
|
||||
<ds-flex-item style="text-align: center">
|
||||
<ds-button
|
||||
v-if="myProfile"
|
||||
v-tooltip="{content: 'Create a new Post', placement: 'left', delay: { show: 500 }}"
|
||||
v-tooltip="{ content: 'Create a new Post', placement: 'left', delay: { show: 500 } }"
|
||||
:path="{ name: 'post-create' }"
|
||||
class="profile-post-add-button"
|
||||
icon="plus"
|
||||
@ -293,28 +184,21 @@
|
||||
/>
|
||||
</ds-flex-item>
|
||||
<template v-if="activePosts.length">
|
||||
<ds-flex-item
|
||||
v-for="post in activePosts"
|
||||
<hc-post-card
|
||||
v-for="(post, index) in activePosts"
|
||||
:key="post.id"
|
||||
:post="post"
|
||||
:width="{ base: '100%', md: '100%', xl: '50%' }"
|
||||
>
|
||||
<hc-post-card :post="post" />
|
||||
</ds-flex-item>
|
||||
@deletePost="user.contributions.splice(index, 1)"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ds-flex-item :width="{ base: '100%' }">
|
||||
<hc-empty
|
||||
margin="xx-large"
|
||||
icon="file"
|
||||
/>
|
||||
<hc-empty margin="xx-large" icon="file" />
|
||||
</ds-flex-item>
|
||||
</template>
|
||||
</ds-flex>
|
||||
<hc-load-more
|
||||
v-if="hasMore"
|
||||
:loading="$apollo.loading"
|
||||
@click="showMoreContributions"
|
||||
/>
|
||||
<hc-load-more v-if="hasMore" :loading="$apollo.loading" @click="showMoreContributions" />
|
||||
</ds-flex-item>
|
||||
</ds-flex>
|
||||
</div>
|
||||
@ -333,6 +217,7 @@ import HcEmpty from '~/components/Empty.vue'
|
||||
import ContentMenu from '~/components/ContentMenu'
|
||||
import HcUpload from '~/components/Upload'
|
||||
import HcAvatar from '~/components/Avatar/Avatar.vue'
|
||||
import PostMutationHelpers from '~/mixins/PostMutationHelpers'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -347,6 +232,7 @@ export default {
|
||||
ContentMenu,
|
||||
HcUpload,
|
||||
},
|
||||
mixins: [PostMutationHelpers],
|
||||
transition: {
|
||||
name: 'slide-up',
|
||||
mode: 'out-in',
|
||||
|
||||
@ -5,16 +5,10 @@
|
||||
</ds-heading>
|
||||
<ds-flex gutter="small">
|
||||
<ds-flex-item :width="{ base: '100%', md: '200px' }">
|
||||
<ds-menu
|
||||
:routes="routes"
|
||||
:is-exact="() => true"
|
||||
/>
|
||||
<ds-menu :routes="routes" :is-exact="() => true" />
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', md: 1 }">
|
||||
<transition
|
||||
name="slide-up"
|
||||
appear
|
||||
>
|
||||
<transition name="slide-up" appear>
|
||||
<nuxt-child />
|
||||
</transition>
|
||||
</ds-flex-item>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.download.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.delete.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
<template>
|
||||
<ds-form
|
||||
v-model="form"
|
||||
@submit="submit"
|
||||
>
|
||||
<ds-form v-model="form" @submit="submit">
|
||||
<ds-card :header="$t('settings.data.name')">
|
||||
<ds-input
|
||||
id="name"
|
||||
@ -32,13 +29,7 @@
|
||||
:placeholder="$t('settings.data.labelBio')"
|
||||
/>
|
||||
<template slot="footer">
|
||||
<ds-button
|
||||
style="float: right;"
|
||||
icon="check"
|
||||
type="submit"
|
||||
:loading="loadingData"
|
||||
primary
|
||||
>
|
||||
<ds-button style="float: right;" icon="check" type="submit" :loading="loadingData" primary>
|
||||
{{ $t('actions.save') }}
|
||||
</ds-button>
|
||||
</template>
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.invites.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.languages.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,9 +1,6 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.organizations.name')">
|
||||
<hc-empty
|
||||
icon="tasks"
|
||||
message="Coming Soon…"
|
||||
/>
|
||||
<hc-empty icon="tasks" message="Coming Soon…" />
|
||||
</ds-card>
|
||||
</template>
|
||||
|
||||
|
||||
@ -1,19 +1,9 @@
|
||||
<template>
|
||||
<ds-card :header="$t('settings.social-media.name')">
|
||||
<ds-space
|
||||
v-if="socialMediaLinks"
|
||||
margin-top="base"
|
||||
margin="x-small"
|
||||
>
|
||||
<ds-space v-if="socialMediaLinks" margin-top="base" margin="x-small">
|
||||
<ds-list>
|
||||
<ds-list-item
|
||||
v-for="link in socialMediaLinks"
|
||||
:key="link.id"
|
||||
>
|
||||
<a
|
||||
:href="link.url"
|
||||
target="_blank"
|
||||
>
|
||||
<ds-list-item v-for="link in socialMediaLinks" :key="link.id">
|
||||
<a :href="link.url" target="_blank">
|
||||
<hc-image
|
||||
:image-props="{ src: link.favicon }"
|
||||
alt="Social Media link"
|
||||
@ -23,15 +13,10 @@
|
||||
{{ link.url }}
|
||||
</a>
|
||||
|
||||
<span class="layout-leave-active">|</span>
|
||||
<ds-icon
|
||||
name="edit"
|
||||
class="layout-leave-active"
|
||||
/>
|
||||
<a
|
||||
name="delete"
|
||||
@click="handleDeleteSocialMedia(link)"
|
||||
>
|
||||
<span class="layout-leave-active">|</span>
|
||||
|
||||
<ds-icon name="edit" class="layout-leave-active" />
|
||||
<a name="delete" @click="handleDeleteSocialMedia(link)">
|
||||
<ds-icon name="trash" />
|
||||
</a>
|
||||
</ds-list-item>
|
||||
@ -43,15 +28,12 @@
|
||||
v-model="value"
|
||||
:placeholder="$t('settings.social-media.placeholder')"
|
||||
name="social-media"
|
||||
:schema="{type: 'url'}"
|
||||
:schema="{ type: 'url' }"
|
||||
/>
|
||||
</div>
|
||||
<ds-space margin-top="base">
|
||||
<div>
|
||||
<ds-button
|
||||
primary
|
||||
@click="handleAddSocialMedia"
|
||||
>
|
||||
<ds-button primary @click="handleAddSocialMedia">
|
||||
{{ $t('settings.social-media.submit') }}
|
||||
</ds-button>
|
||||
</div>
|
||||
|
||||
@ -8856,10 +8856,10 @@ prettier@^1.15.2:
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008"
|
||||
integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw==
|
||||
|
||||
prettier@~1.14.3:
|
||||
version "1.14.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895"
|
||||
integrity sha512-qZDVnCrnpsRJJq5nSsiHCE3BYMED2OtsI+cmzIzF1QIfqm5ALf8tEJcO27zV1gKNKRPdhjO0dNWnrzssDQ1tFg==
|
||||
prettier@~1.17.1:
|
||||
version "1.17.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.1.tgz#ed64b4e93e370cb8a25b9ef7fef3e4fd1c0995db"
|
||||
integrity sha512-TzGRNvuUSmPgwivDqkZ9tM/qTGW9hqDKWOE9YHiyQdixlKbv7kvEqsmDPrcHJTKwthU774TQwZXVtaQ/mMsvjg==
|
||||
|
||||
pretty-bytes@^5.2.0:
|
||||
version "5.2.0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user