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} 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 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 import locale
from urllib.parse import parse_qs from urllib.parse import parse_qs
from pathlib import Path
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from app.constants import * from app.constants import *
app = FastAPI() app = FastAPI()
templates = Jinja2Templates(directory="templates") 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: try:
locale.setlocale(locale.LC_TIME, "ru_RU") locale.setlocale(locale.LC_TIME, "ru_RU")
except Exception as e: 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)
date = datetime.fromisoformat(value) return date.strftime(format)
return date.strftime(format)
except Exception as e:
return value
statuses = { statuses = {
"1": "Новая", "1": "STATE_NEW",
"2": "Ждет выполнения", "2": "STATE_PENDING",
"3": "Выполняется", "3": "STATE_IN_PROGRESS",
"4": "Требуется контроль", "4": "STATE_SUPPOSEDLY_COMPLETED",
"5": "Завершена", "5": "STATE_COMPLETED",
"6": "Отложена", "6": "STATE_DEFERRED",
"7": "Отклонена", "7": "STATE_DECLINED",
} }
@ -67,7 +43,6 @@ def format_status(value):
res = statuses[value] if value in statuses else value res = statuses[value] if value in statuses else value
return res return res
# Регистрируем фильтр в Jinja2Templates # Регистрируем фильтр в Jinja2Templates
templates.env.filters["format_status"] = format_status templates.env.filters["format_status"] = format_status
templates.env.filters["format_datetime"] = format_datetime templates.env.filters["format_datetime"] = format_datetime
@ -158,41 +133,6 @@ async def tg_intgr_get(request: Request):
return {"status": "error"} 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") @app.post("/deal_tab")
async def deal_tab( async def deal_tab(
request: Request, request: Request,
@ -207,12 +147,32 @@ async def deal_tab(
result = parse_qs(b_str) result = parse_qs(b_str)
q = [DOMAIN, PROTOCOL, LANG, APP_SID] 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) deal_id = extract_id(result)
if not deal_id: if not deal_id:
raise ("not deal id")
deal_id = 49 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"]) # logger.info(task_data_json["result"]["tasks"])
parts = WEBHOOK.split("/") parts = WEBHOOK.split("/")
@ -220,58 +180,10 @@ async def deal_tab(
return templates.TemplateResponse( return templates.TemplateResponse(
request=request, request=request,
name="deal_tab.html", 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} return {"status": "success", "result": task_data_json}
except Exception as e: except Exception as e:
logger.error(e) logger.error(e)
return {"status": "error"} 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"
}