mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
⚡ Add task resource to Microsoft Team Node (#1403)
* ⚡ Add task resource to Microsoft Team Node * ⚡ Minor improvement on Microsoft Team Node Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
parent
4150ae00c7
commit
7c2755c970
|
@ -26,11 +26,16 @@ import {
|
|||
channelMessageOperations,
|
||||
} from './ChannelMessageDescription';
|
||||
|
||||
import {
|
||||
taskFields,
|
||||
taskOperations,
|
||||
} from './TaskDescription';
|
||||
|
||||
export class MicrosoftTeams implements INodeType {
|
||||
description: INodeTypeDescription = {
|
||||
displayName: 'Microsoft Teams',
|
||||
name: 'microsoftTeams',
|
||||
icon: 'file:teams.png',
|
||||
icon: 'file:teams.svg',
|
||||
group: ['input'],
|
||||
version: 1,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
|
@ -61,6 +66,10 @@ export class MicrosoftTeams implements INodeType {
|
|||
name: 'Channel Message (Beta)',
|
||||
value: 'channelMessage',
|
||||
},
|
||||
{
|
||||
name: 'Task',
|
||||
value: 'task',
|
||||
},
|
||||
],
|
||||
default: 'channel',
|
||||
description: 'The resource to operate on.',
|
||||
|
@ -71,6 +80,9 @@ export class MicrosoftTeams implements INodeType {
|
|||
/// MESSAGE
|
||||
...channelMessageOperations,
|
||||
...channelMessageFields,
|
||||
///TASK
|
||||
...taskOperations,
|
||||
...taskFields,
|
||||
],
|
||||
};
|
||||
|
||||
|
@ -107,6 +119,77 @@ export class MicrosoftTeams implements INodeType {
|
|||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the groups to display them to user so that he can
|
||||
// select them easily
|
||||
async getGroups(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const { value } = await microsoftApiRequest.call(this, 'GET', '/v1.0/groups');
|
||||
for (const group of value) {
|
||||
returnData.push({
|
||||
name: group.mail,
|
||||
value: group.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the plans to display them to user so that he can
|
||||
// select them easily
|
||||
async getPlans(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const groupId = this.getCurrentNodeParameter('groupId') as string;
|
||||
const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/groups/${groupId}/planner/plans`);
|
||||
for (const plan of value) {
|
||||
returnData.push({
|
||||
name: plan.title,
|
||||
value: plan.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the plans to display them to user so that he can
|
||||
// select them easily
|
||||
async getBuckets(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const planId = this.getCurrentNodeParameter('planId') as string;
|
||||
const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/plans/${planId}/buckets`);
|
||||
for (const bucket of value) {
|
||||
returnData.push({
|
||||
name: bucket.name,
|
||||
value: bucket.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the plans to display them to user so that he can
|
||||
// select them easily
|
||||
async getMembers(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const groupId = this.getCurrentNodeParameter('groupId') as string;
|
||||
const { value } = await microsoftApiRequest.call(this, 'GET', `/v1.0/groups/${groupId}/members`);
|
||||
for (const member of value) {
|
||||
returnData.push({
|
||||
name: member.displayName,
|
||||
value: member.id,
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
// Get all the labels to display them to user so that he can
|
||||
// select them easily
|
||||
async getLabels(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
|
||||
const returnData: INodePropertyOptions[] = [];
|
||||
const planId = this.getCurrentNodeParameter('planId') as string;
|
||||
const { categoryDescriptions } = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/plans/${planId}/details`);
|
||||
for (const key of Object.keys(categoryDescriptions)) {
|
||||
if (categoryDescriptions[key] !== null) {
|
||||
returnData.push({
|
||||
name: categoryDescriptions[key],
|
||||
value: key,
|
||||
});
|
||||
}
|
||||
}
|
||||
return returnData;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -206,6 +289,88 @@ export class MicrosoftTeams implements INodeType {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (resource === 'task') {
|
||||
//https://docs.microsoft.com/en-us/graph/api/planner-post-tasks?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'create') {
|
||||
const planId = this.getNodeParameter('planId', i) as string;
|
||||
const bucketId = this.getNodeParameter('bucketId', i) as string;
|
||||
const title = this.getNodeParameter('title', i) as string;
|
||||
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
|
||||
const body: IDataObject = {
|
||||
planId,
|
||||
bucketId,
|
||||
title,
|
||||
};
|
||||
Object.assign(body, additionalFields);
|
||||
|
||||
if (body.assignedTo) {
|
||||
body.assignments = {
|
||||
[body.assignedTo as string]: {
|
||||
'@odata.type': 'microsoft.graph.plannerAssignment',
|
||||
'orderHint': ' !',
|
||||
},
|
||||
};
|
||||
delete body.assignedTo;
|
||||
}
|
||||
|
||||
if (Array.isArray(body.labels)) {
|
||||
body.appliedCategories = (body.labels as string[]).map((label) => ({ [label]: true }));
|
||||
}
|
||||
|
||||
responseData = await microsoftApiRequest.call(this, 'POST', `/v1.0/planner/tasks`, body);
|
||||
}
|
||||
//https://docs.microsoft.com/en-us/graph/api/plannertask-delete?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'delete') {
|
||||
const taskId = this.getNodeParameter('taskId', i) as string;
|
||||
const task = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/tasks/${taskId}`);
|
||||
responseData = await microsoftApiRequest.call(this, 'DELETE', `/v1.0/planner/tasks/${taskId}`, {}, {}, undefined, { 'If-Match': task['@odata.etag'] });
|
||||
responseData = { success: true };
|
||||
}
|
||||
//https://docs.microsoft.com/en-us/graph/api/plannertask-get?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'get') {
|
||||
const taskId = this.getNodeParameter('taskId', i) as string;
|
||||
responseData = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/tasks/${taskId}`);
|
||||
}
|
||||
//https://docs.microsoft.com/en-us/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'getAll') {
|
||||
const memberId = this.getNodeParameter('memberId', i) as string;
|
||||
const returnAll = this.getNodeParameter('returnAll', i) as boolean;
|
||||
if (returnAll) {
|
||||
responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`);
|
||||
} else {
|
||||
qs.limit = this.getNodeParameter('limit', i) as number;
|
||||
responseData = await microsoftApiRequestAllItems.call(this, 'value', 'GET', `/v1.0/users/${memberId}/planner/tasks`, {});
|
||||
responseData = responseData.splice(0, qs.limit);
|
||||
}
|
||||
}
|
||||
//https://docs.microsoft.com/en-us/graph/api/plannertask-update?view=graph-rest-1.0&tabs=http
|
||||
if (operation === 'update') {
|
||||
const taskId = this.getNodeParameter('taskId', i) as string;
|
||||
const updateFields = this.getNodeParameter('updateFields', i) as IDataObject;
|
||||
const body: IDataObject = {};
|
||||
Object.assign(body, updateFields);
|
||||
|
||||
if (body.assignedTo) {
|
||||
body.assignments = {
|
||||
[body.assignedTo as string]: {
|
||||
'@odata.type': 'microsoft.graph.plannerAssignment',
|
||||
'orderHint': ' !',
|
||||
},
|
||||
};
|
||||
delete body.assignedTo;
|
||||
}
|
||||
|
||||
if (Array.isArray(body.labels)) {
|
||||
body.appliedCategories = (body.labels as string[]).map((label) => ({ [label]: true }));
|
||||
}
|
||||
|
||||
const task = await microsoftApiRequest.call(this, 'GET', `/v1.0/planner/tasks/${taskId}`);
|
||||
|
||||
responseData = await microsoftApiRequest.call(this, 'PATCH', `/v1.0/planner/tasks/${taskId}`, body, {}, undefined, { 'If-Match': task['@odata.etag'] });
|
||||
|
||||
responseData = { success: true };
|
||||
}
|
||||
}
|
||||
if (Array.isArray(responseData)) {
|
||||
returnData.push.apply(returnData, responseData as IDataObject[]);
|
||||
} else {
|
||||
|
|
451
packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts
Normal file
451
packages/nodes-base/nodes/Microsoft/Teams/TaskDescription.ts
Normal file
|
@ -0,0 +1,451 @@
|
|||
import {
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
export const taskOperations = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Create',
|
||||
value: 'create',
|
||||
description: 'Create a task',
|
||||
},
|
||||
{
|
||||
name: 'Delete',
|
||||
value: 'delete',
|
||||
description: 'Delete a task',
|
||||
},
|
||||
{
|
||||
name: 'Get',
|
||||
value: 'get',
|
||||
description: 'Get a task',
|
||||
},
|
||||
{
|
||||
name: 'Get All',
|
||||
value: 'getAll',
|
||||
description: 'Get all tasks',
|
||||
},
|
||||
{
|
||||
name: 'Update',
|
||||
value: 'update',
|
||||
description: 'Update a task',
|
||||
},
|
||||
],
|
||||
default: 'create',
|
||||
description: 'The operation to perform.',
|
||||
},
|
||||
] as INodeProperties[];
|
||||
|
||||
export const taskFields = [
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* task:create */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Group ID',
|
||||
name: 'groupId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getGroups',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Group ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Plan ID',
|
||||
name: 'planId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getPlans',
|
||||
loadOptionsDependsOn: [
|
||||
'groupId',
|
||||
],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The ID of the Plan.',
|
||||
},
|
||||
{
|
||||
displayName: 'Bucket ID',
|
||||
name: 'bucketId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getBuckets',
|
||||
loadOptionsDependsOn: [
|
||||
'planId',
|
||||
],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The ID of the Bucket.',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Title of the task.',
|
||||
},
|
||||
{
|
||||
displayName: 'Additional Fields',
|
||||
name: 'additionalFields',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'create',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Assigned To',
|
||||
name: 'assignedTo',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getMembers',
|
||||
loadOptionsDependsOn: [
|
||||
'groupId',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Due Date Time',
|
||||
name: 'dueDateTime',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
loadOptionsDependsOn: [
|
||||
'planId',
|
||||
],
|
||||
},
|
||||
default: [],
|
||||
description: `Percentage of task completion. When set to 100, the task is considered completed.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Percent Complete',
|
||||
name: 'percentComplete',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 0,
|
||||
description: `Percentage of task completion. When set to 100, the task is considered completed.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* task:delete */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Task ID',
|
||||
name: 'taskId',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'delete',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Task ID',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* task:get */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Task ID',
|
||||
name: 'taskId',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'get',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Task ID',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* task:getAll */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Group ID',
|
||||
name: 'groupId',
|
||||
required: true,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getGroups',
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Group ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Member ID',
|
||||
name: 'memberId',
|
||||
required: false,
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getMembers',
|
||||
loadOptionsDependsOn: [
|
||||
'groupId',
|
||||
],
|
||||
},
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'Member ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Return All',
|
||||
name: 'returnAll',
|
||||
type: 'boolean',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: false,
|
||||
description: 'If all results should be returned or only up to a given limit.',
|
||||
},
|
||||
{
|
||||
displayName: 'Limit',
|
||||
name: 'limit',
|
||||
type: 'number',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'getAll',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
returnAll: [
|
||||
false,
|
||||
],
|
||||
},
|
||||
},
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
maxValue: 500,
|
||||
},
|
||||
default: 100,
|
||||
description: 'How many results to return.',
|
||||
},
|
||||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
/* task:update */
|
||||
/* -------------------------------------------------------------------------- */
|
||||
{
|
||||
displayName: 'Task ID',
|
||||
name: 'taskId',
|
||||
required: true,
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: '',
|
||||
description: 'The ID of the Task.',
|
||||
},
|
||||
{
|
||||
displayName: 'Update Fields',
|
||||
name: 'updateFields',
|
||||
type: 'collection',
|
||||
displayOptions: {
|
||||
show: {
|
||||
operation: [
|
||||
'update',
|
||||
],
|
||||
resource: [
|
||||
'task',
|
||||
],
|
||||
},
|
||||
},
|
||||
default: {},
|
||||
placeholder: 'Add Field',
|
||||
options: [
|
||||
{
|
||||
displayName: 'Assigned To',
|
||||
name: 'assignedTo',
|
||||
type: 'options',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getMembers',
|
||||
loadOptionsDependsOn: [
|
||||
'groupId',
|
||||
],
|
||||
},
|
||||
default: '',
|
||||
description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Bucket ID',
|
||||
name: 'bucketId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Channel name as it will appear to the user in Microsoft Teams.',
|
||||
},
|
||||
{
|
||||
displayName: 'Due Date Time',
|
||||
name: 'dueDateTime',
|
||||
type: 'dateTime',
|
||||
default: '',
|
||||
description: `Date and time at which the task is due. The Timestamp type represents date and time information using ISO 8601 format and is always in UTC time.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Group ID',
|
||||
name: 'groupId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Group ID',
|
||||
},
|
||||
{
|
||||
displayName: 'Labels',
|
||||
name: 'labels',
|
||||
type: 'multiOptions',
|
||||
typeOptions: {
|
||||
loadOptionsMethod: 'getLabels',
|
||||
loadOptionsDependsOn: [
|
||||
'planId',
|
||||
],
|
||||
},
|
||||
default: [],
|
||||
description: `Percentage of task completion. When set to 100, the task is considered completed.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Percent Complete',
|
||||
name: 'percentComplete',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 0,
|
||||
maxValue: 100,
|
||||
},
|
||||
default: 0,
|
||||
description: `Percentage of task completion. When set to 100, the task is considered completed.`,
|
||||
},
|
||||
{
|
||||
displayName: 'Plan ID',
|
||||
name: 'planId',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Channel name as it will appear to the user in Microsoft Teams.',
|
||||
},
|
||||
{
|
||||
displayName: 'Title',
|
||||
name: 'title',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'Title of the task.',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as INodeProperties[];
|
Binary file not shown.
Before Width: | Height: | Size: 725 B |
21
packages/nodes-base/nodes/Microsoft/Teams/teams.svg
Normal file
21
packages/nodes-base/nodes/Microsoft/Teams/teams.svg
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 2381.4 2354.5" style="enable-background:new 0 0 2381.4 2354.5;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#5558AF;}
|
||||
.st1{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M2015.6,899.2c19.5,19.5,42.5,35,67.9,45.8c53,22.2,112.7,22.2,165.8,0c51.2-21.8,92-62.5,113.7-113.7
|
||||
c22.2-53,22.2-112.7,0-165.8c-21.8-51.2-62.5-92-113.7-113.7c-53-22.2-112.7-22.2-165.8,0c-51.2,21.8-92,62.5-113.7,113.7
|
||||
c-22.2,53-22.2,112.7,0,165.8C1980.6,856.6,1996.2,879.7,2015.6,899.2L2015.6,899.2z M1953.2,1097v642.1h107
|
||||
c36.8-0.2,73.4-3.6,109.5-10.4c36.3-6.4,71.3-18.6,103.7-36.2c30.6-16.6,57-40,77.3-68.2c21.3-31.3,32-68.6,30.5-106.5V1097H1953.2
|
||||
z M1606.4,827.8c28.4,0.2,56.6-5.5,82.8-16.7c51.2-21.8,91.9-62.5,113.6-113.7c22.2-53,22.2-112.7-0.1-165.8
|
||||
c-21.8-51.2-62.5-92-113.7-113.7c-26.2-11.2-54.4-16.9-82.9-16.7c-28.3-0.2-56.3,5.5-82.3,16.7c-19.4,8.3-25.5,19.1-52.2,32.1v329
|
||||
c26.8,13.1,32.8,23.8,52.2,32.1C1549.9,822.4,1578,828,1606.4,827.8L1606.4,827.8z M1471.6,1908.9c26.8,5.8,36.4,10.3,55.4,12.9
|
||||
c20.8,3,41.8,4.5,62.8,4.6c32.4-0.2,64.8-3.6,96.5-10.4c32.3-6.5,63.3-18.6,91.5-35.7c27.7-17,51-40.2,68.2-67.7
|
||||
c19-32.1,28.3-69.1,26.9-106.4v-743h-401.3V1908.9z M0,2113.7l1391.3,240.8V0L0,240.8V2113.7z"/>
|
||||
</g>
|
||||
<path class="st1" d="M1016.7,722.4l-642.1,39.1v148.1l240.8-9.7v686.7l160.5,9.4V893.6l240.8-10.7V722.4z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
Loading…
Reference in a new issue