Compare commits

..

1 Commits

Author SHA1 Message Date
ksenia_mikhailova 85bdb02332 Merge pull request 'bx-2052-deal-tab' (#2) from bx-2052-deal-tab into main
Reviewed-on: #2
2024-11-19 13:58:59 +03:00
13 changed files with 33 additions and 11681 deletions

View File

@ -19,6 +19,4 @@ RUN poetry install
COPY . ${WORKING_DIR}
RUN cd front && npm install && npm run generate
RUN cd ..
CMD poetry run uvicorn app.main:app --host 0.0.0.0 --port 80

View File

@ -5,35 +5,14 @@ from datetime import datetime
import locale
from urllib.parse import parse_qs
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from app.constants import *
app = FastAPI()
templates = Jinja2Templates(directory="templates")
origins = [
"http://localhost",
"http://localhost:3000",
"http://localhost:3001",
# "http://192.168.100.242"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
try:
locale.setlocale(locale.LC_TIME, "ru_RU")
except Exception as e:
@ -41,23 +20,20 @@ except Exception as e:
# Создаем кастомный фильтр для форматирования дат
def format_datetime(value, format="%d %B %Y"):
def format_datetime(value, format="%d %B %Y, %H:%M:%S"):
"""Форматирует дату в человекочитаемый вид."""
try:
date = datetime.fromisoformat(value)
return date.strftime(format)
except Exception as e:
return value
date = datetime.fromisoformat(value)
return date.strftime(format)
statuses = {
"1": "Новая",
"2": "Ждет выполнения",
"3": "Выполняется",
"4": "Требуется контроль",
"5": "Завершена",
"6": "Отложена",
"7": "Отклонена",
"1": "STATE_NEW",
"2": "STATE_PENDING",
"3": "STATE_IN_PROGRESS",
"4": "STATE_SUPPOSEDLY_COMPLETED",
"5": "STATE_COMPLETED",
"6": "STATE_DEFERRED",
"7": "STATE_DECLINED",
}
@ -67,7 +43,6 @@ def format_status(value):
res = statuses[value] if value in statuses else value
return res
# Регистрируем фильтр в Jinja2Templates
templates.env.filters["format_status"] = format_status
templates.env.filters["format_datetime"] = format_datetime
@ -158,41 +133,6 @@ async def tg_intgr_get(request: Request):
return {"status": "error"}
def extract_id(params):
json_strings = params.get("PLACEMENT_OPTIONS", [])
for json_str in json_strings:
try:
data = json.loads(json_str)
if "ID" in data:
return data["ID"]
except json.JSONDecodeError:
print(f"Ошибка: Некорректная JSON-строка - {json_str}")
def get_task_by_deal(deal_id):
select = [
"ID",
"TITLE",
"RESPONSIBLE_ID",
"CREATED_DATE",
"DEADLINE",
"STATUS",
"GROUP_ID",
]
get_task_hook = f"{WEBHOOK}/tasks.task.list?{'&'.join([f'select[]={s}' for s in select])}&filter[UF_CRM_TASK]=D_{deal_id}"
task_data = requests.get(get_task_hook)
task_data_json = task_data.json()
result = task_data_json["result"]["tasks"]
while len(task_data_json["result"]["tasks"]) == 50 and len(result) < 50 * 4:
limit = (len(result) // 50) * 50
task_data = requests.get(get_task_hook + f"&start={limit}")
task_data_json = task_data.json()
result += task_data_json["result"]["tasks"]
return result
@app.post("/deal_tab")
async def deal_tab(
request: Request,
@ -207,12 +147,32 @@ async def deal_tab(
result = parse_qs(b_str)
q = [DOMAIN, PROTOCOL, LANG, APP_SID]
def extract_id(params):
json_strings = params.get("PLACEMENT_OPTIONS", [])
for json_str in json_strings:
try:
data = json.loads(json_str)
if "ID" in data:
return data["ID"]
except json.JSONDecodeError:
print(f"Ошибка: Некорректная JSON-строка - {json_str}")
deal_id = extract_id(result)
if not deal_id:
raise ("not deal id")
deal_id = 49
result = get_task_by_deal(deal_id)
select = [
"ID",
"TITLE",
"RESPONSIBLE_ID",
"CREATED_DATE",
"DEADLINE",
"STATUS",
"GROUP_ID",
]
get_task_hook = f"{WEBHOOK}/tasks.task.list?{'&'.join([f'select[]={s}' for s in select])}&filter[UF_CRM_TASK]=D_{deal_id}"
task_data = requests.get(get_task_hook)
task_data_json = task_data.json()
# logger.info(task_data_json["result"]["tasks"])
parts = WEBHOOK.split("/")
@ -220,58 +180,10 @@ async def deal_tab(
return templates.TemplateResponse(
request=request,
name="deal_tab.html",
context={"tasks": result, "domain": domain},
context={"tasks": task_data_json["result"]["tasks"], "domain": domain},
)
return {"status": "success", "result": task_data_json}
except Exception as e:
logger.error(e)
return {"status": "error"}
@app.post("/widget/deal_tab/{deal_id}")
async def deal_tab(request: Request, deal_id):
try:
result = get_task_by_deal(deal_id)
# logger.info(task_data_json["result"]["tasks"])
parts = WEBHOOK.split("/")
domain = f"https://{parts[2]}"
return {
"status": "success",
"result": result,
"format": {"domain": domain, "statuses": statuses},
}
except Exception as e:
logger.error(e)
return {"status": "error"}
app.mount("/widget", StaticFiles(directory="front/dist", html=True), name="static")
NUXT_DIST = Path("front/dist")
@app.post("/widget")
async def handle_widget_post(request: Request):
# Обработка данных POST-запроса
# data = await request.json()
# Загружаем сгенерированный HTML (например, index.html)
body = await request.body()
b_str = body.decode()
result = parse_qs(b_str)
deal_id = extract_id(result)
if not deal_id:
raise ("not deal id")
deal_id = 49
logger.info(result)
html_file = NUXT_DIST / "deal/index.html"
if html_file.exists():
html_content = html_file.read_text()
html_content = html_content.replace("data_deal_id:0", f"data_deal_id:{deal_id}")
return HTMLResponse(content=html_content, status_code=200)
return {"error": "Nuxt.js page not found"}, 404

24
front/.gitignore vendored
View File

@ -1,24 +0,0 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist
# Node dependencies
node_modules
# Logs
logs
*.log
# Misc
.DS_Store
.fleet
.idea
# Local env files
.env
.env.*
!.env.example

View File

@ -1,75 +0,0 @@
# Nuxt Minimal Starter
Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Setup
Make sure to install dependencies:
```bash
# npm
npm install
# pnpm
pnpm install
# yarn
yarn install
# bun
bun install
```
## Development Server
Start the development server on `http://localhost:3000`:
```bash
# npm
npm run dev
# pnpm
pnpm dev
# yarn
yarn dev
# bun
bun run dev
```
## Production
Build the application for production:
```bash
# npm
npm run build
# pnpm
pnpm build
# yarn
yarn build
# bun
bun run build
```
Locally preview production build:
```bash
# npm
npm run preview
# pnpm
pnpm preview
# yarn
yarn preview
# bun
bun run preview
```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

View File

@ -1,3 +0,0 @@
<template>
<NuxtPage/>
</template>

View File

@ -1,23 +0,0 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-04-03',
devtools: { enabled: true },
app: {
baseURL: '/widget/', // Базовый путь
// buildAssetsDir: '/widget/_nuxt/', // Путь для статических файлов
},
runtimeConfig: {
public: {
apiBase: process.env.NODE_ENV == 'development' ? 'http://localhost:8000' : '',
data_deal_id: 0
}
},
colorMode: {
preference: 'light'
},
modules: ['@nuxt/ui'],
})

11296
front/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
{
"name": "nuxt-app",
"private": true,
"type": "module",
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"dependencies": {
"@nuxt/ui": "^2.19.2",
"nuxt": "^3.14.1592",
"vue": "latest",
"vue-router": "latest"
}
}

View File

@ -1,111 +0,0 @@
<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
const data = ref()
const formatter = ref()
export interface Data {
status: string;
result: Result[];
format: {
domain: string;
statuses: { [key: string]: string };
}
}
export interface Result {
id: string;
title: string;
responsibleId: string;
createdDate: Date;
deadline: Date;
status: string;
groupId: string;
group: any[] | GroupClass;
responsible: string;
subStatus: string;
}
export interface GroupClass {
id: string;
name: string;
opened: boolean;
membersCount: number;
image: string;
additionalData: any[];
}
const loadData = async (deal_id: number) => {
const res = await $fetch<Data>(`${config.public.apiBase}/widget/deal_tab/${deal_id}`, { method: 'POST' })
if (res.status == 'success') {
data.value = res.result
formatter.value = res.format
}
}
const columns = [{
key: 'id',
label: 'ID',
sortable: true
}, {
key: 'title',
label: 'Название'
}, {
key: 'status',
label: 'Статус',
sortable: true
}, {
key: 'responsible',
label: 'Исполнитель',
}, {
key: 'createdDate',
label: 'Создана',
sortable: true
}, {
key: 'deadline',
label: 'Крайний срок',
sortable: true
}]
onMounted(() => {
const deal_id = config.public.data_deal_id
if (!deal_id) return
loadData(deal_id)
})
</script>
<template>
<UTable :rows="data" :columns="columns">
<template #responsible-data="{ row }">
{{ row.responsible.name }}
</template>
<template #deadline-data="{ row }">
{{ new Date(Date.parse(row.deadline)).toLocaleDateString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
}) }}
</template>
<template #createdDate-data="{ row }">
{{ new Date(Date.parse(row.createdDate)).toLocaleDateString('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
}) }}
</template>
<template #status-data="{ row }">
{{ formatter.statuses[row.status] }}
</template>
<template #title-data="{ row }">
<div class="max-w-screen-md break-words whitespace-break-spaces">
<a :href="`${formatter.domain}/workgroups/group/${row.groupId}/tasks/task/view/${row.id}/`"
target="_blank" class="text-primary-600 underline hover:no-underline">
{{ row.title }}
</a>
</div>
</template>
</UTable>
</template>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1 +0,0 @@

View File

@ -1,3 +0,0 @@
{
"extends": "../.nuxt/tsconfig.server.json"
}

View File

@ -1,4 +0,0 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}